【问题标题】:Wrap character in String, excluding a Link tag with Javascript Regex在字符串中包装字符,不包括带有 Javascript 正则表达式的链接标记
【发布时间】:2013-12-19 18:39:01
【问题描述】:

编辑

这是我必须做的......

想象一下,如果我有一个带有一些 html 标签的文本(它仍然是一个字符串):

var string = '<p>Hello, my name is Mauricio</p><p>Hi, my name is Patricia</p><p class="warn">Yeah, My name is Carl</p><a href="#"><img src="#" /></a>';

我想把所有的字母“a”都用

 <span class="ui-match"></span>

但我不能替换标签中的任何东西,也不能替换它里面的东西,也不能替换

元素。

因此,如果我想包装该字符串中的所有字母“a”,它将像这样返回:

<p>Hello, my n<span class="ui-match">a</span>me is M<span class="ui-match">a</span>uricio</p><p>Hi, my n<span class="ui-match">a</span>me is P<span class="ui-match">a</span>trici<span class="ui-match">a</span></p><p class="warn">Ye<span class="ui-match">a</span>h, My n<span class="ui-match">a</span>me is C<span class="ui-match">a</span>rl</p><a href="#"><img src="#" /></a>

所有用

包裹的字母“a”
 <span class="ui-match"></span>

,但链接和段落都没有。

这个字符串也来自一个 API,所以它是动态的……我正在搜索的这个字母是动态的,所以它可以是“a”或“abc”……它不能区分大小写

谢谢

【问题讨论】:

  • 是只能使用正则表达式,还是可以结合其他函数使用正则表达式?
  • 你可以结合一切... :)

标签: javascript regex


【解决方案1】:

我建议您将问题拆分为 2 个较小的问题:

  1. 抓取所有标签的文本内容。
  2. &lt;span class="ui-match"&gt;&lt;/span&gt; 包装字符

使用RegExp to parse HTML is a bad idea,但在这种情况下,由于您似乎控制输入结构,您可以使用它来简化逻辑。

使用单个 RegExp 将非常困难,因此最好使用 2 String#replace 而不是 1。一个通用的实现是这样的:

function replaceHtmlContent(str, match, replaceFn) {
  // we use the "g" and "i" flags to make it replace all occurrences and ignore case
  var re = new RegExp(match, 'gi');
  // this RegExp will match any char sequence that doesn't contain "<" or ">"
  // and that is followed by a tag
  return str.replace(/([^<>]+)(?=<[^>]+>)/g, function(s, content){
    return content.replace(re, replaceFn);
  });
}

可以抽象为:

function wrapMatch(src, match) {
  return replaceHtmlContent(src, match, function(str){
    return '<span class="ui-match">'+ str +'</span>';
  });
}

后来使用如下:

var output = wrapMatch(input, 'a');

这将给出示例输入的预期结果。

演示: http://jsbin.com/ovUFEsas/4/edit

【讨论】:

  • 伙计,你救了我的命哈哈哈你太棒了!
【解决方案2】:

如果不使用正则表达式,使用 DOM 节点会更快:

var div = document.createElement('div'),
    children;

div.innerHTML = 'Hello, my name is mauricio, and i like <a href="#">Star Wars</a>';
children = div.childNodes;

for (var i = 0, len = children.length; i < len; i++) {
    console.log(children[i]);
    if (children[i].nodeType === 3) {
        children[i].nodeValue = children[i].nodeValue.replace(/a/g, 'R');
    }
}

console.log(div.innerHTML);

注意:我在这里使用innerHTML属性作为示例方式,但是由于性能相当低,不建议利用它。

演示: http://jsfiddle.net/N7rdW/


更新:

根据您的更新,对于HERE 的另一个问题,您最好使用我的回答中的方法。代码有点复杂,但速度相当快(不要记住innerHTML的用法):

var div = document.createElement('div');
div.innerHTML = 'Hello, my name is mauricio, and i like <a href="#">Star Wars</a>';

for (var i = 0, children = div.childNodes, len = children.length; i < len; i++) {
    var child = children[i];
    if (child.nodeType === 3 && child.nodeValue.indexOf('a') > -1) {
        var segments = child.nodeValue.split('a');
        for (var k = 0, lk = segments.length; k < lk; k++) {
            div.insertBefore(document.createTextNode(segments[k]), child);
            if (k < lk - 1) {
                var span = document.createElement('span');
                span.className = 'ui-match';
                span.appendChild(document.createTextNode('R'));
                div.insertBefore(span, child);
            }
        }
        div.removeChild(child);
    }
}

console.log(div.innerHTML);

演示: http://jsfiddle.net/T4ZXA/6/

【讨论】:

  • 感谢远见!很抱歉,我的问题表述错误……我刚刚更新了它……我不想替换,我想用标签包装
  • @MauricioSoares 那么你最好使用我的其他答案来解决这里的类似问题:stackoverflow.com/a/16239900/1249581
  • 嘿@VisioN,您介意创建一个适用于我的字符串的示例吗?我不认为我得到你的答案真的很好......另外,有没有办法在不创建元素的情况下做到这一点?只操作字符串?
  • @MauricioSoares 请查看我的更新答案。仅供参考,你应该never try to parse HTML with regex
  • 谢谢,我正在测试你的答案... :)
【解决方案3】:

此解决方案是否符合您的要求?

string = string.replace(/a(?![^<]*?>)/g, '<span class="ui-match">a</span>');

关于(?![^&lt;]*?&gt;)的一点帮助(大致是:“一些文字后面没有&amp;gt;”):

(?!...)   not followed by
[^<]*     any char except "<", zero or more times
?>        until next ">"

包裹在一个函数中:

function replace(html, text, replacement) {
    // RegExp.escape : http://stackoverflow.com/q/3561493/1636522
    var re = new RegExp('(' + RegExp.escape(text) + ')(?![^<]*?>)', 'g');
    return html.replace(re, replacement);
}
var html = '<a class="azerty"> azerty &lt; azerty </a>';
html = replace(html, 'azerty', '<b>$1</b>');
// "<a class="azerty"> <b>azerty</b> &lt; <b>azerty</b> </a>"

【讨论】:

  • 感谢您@wared 的回答...但 Miller Meideiros 的回答似乎更简单,而且效果很好。但我真的很感谢你的帮助。谢谢!
  • @MauricioSoares 不客气 :D 请注意,您的选择同样会失败。
  • @MauricioSoares 忘记我之前告诉过你的事情,实际上标签之间不能有任何&amp;lt;&amp;gt;,因为我们必须分别用HTML 实体替换它们,&amp;lt;&amp;gt;。所以,显然,“你的选择以同样的方式失败”......我必须完全审查我的答案^^'愚蠢的我。
  • @MauricioSoares 我恢复到第一个版本:D
  • @MauricioSoares 并添加了一个方便的功能:)
猜你喜欢
  • 2023-03-14
  • 1970-01-01
  • 2019-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-24
  • 1970-01-01
相关资源
最近更新 更多