【问题标题】:Javascript regex neglect div span tagsJavascript 正则表达式忽略 div span 标签
【发布时间】:2020-03-30 16:41:29
【问题描述】:

我有以下文字

<span> is an </span>

我想将 an 更改为 a,我使用下面的正则表达式模式来做到这一点。

const regExFinder = new RegExp("an", 'gi');
const sourceHTML = "<span> is an </span>";
sourceHTML.replace(regExFinder, `$&`);

但是输出是这样的。谁能告诉我如何忽略任何标签,只更改标签内的文本。

<spa> is a </spa>

如果我的源 HTML 如下所示:

<div> an <span> is an </span></div>

【问题讨论】:

  • 您不应该使用正则表达式来操作 HTML 代码。使用 DOM 模型并改为对实际的 &lt;span&gt; 元素进行操作。
  • 任何代码示例如何做到这一点?

标签: javascript html regex regex-group


【解决方案1】:

你有几个选择。

const str = "<div> an <span> is an </span></div>";
// method 1: negative lookaheads (probably the best for regex)
str.replace(/an(?![^<>]*>)/gi, "a");
// method 2: rely on having a space after the "an" (not reliable)
str.replace(/an /gi, "a ")
// method 3: rely on "an" being its own word (depends on the situation)
str.replace(/\ban/gi, "a")

【讨论】:

    【解决方案2】:

    我将整个字符串解析为一个DOM元素,然后遍历所有span元素,将其内容从“an”更改为“a”,正则表达式中的元字符\b表示单词边界。

    编辑:

    深入挖掘之后,我可以现在对所有文本节点进行操作并更改相关字符串:

    var html='<div> an <span> is an </span>apple and this <span> is a </span> banana.</div>';
    
    var b=document.createElement('body');
    b.innerHTML=html;
    //               use the "optional filter function" to do the changes:
    getTextNodesIn(b,n=>n.textContent=n.textContent.replace(/\ban\b/g,'a'));
    
    // output:
    console.log(b.innerHTML);
    
    // I just realised that I can also use Chris West's original function:
    // https://cwestblog.com/2014/03/14/javascript-getting-all-text-nodes/
    function getTextNodesIn(elem, opt_fnFilter) {
      var textNodes = [];
      if (elem) {
        for (var nodes = elem.childNodes, i = nodes.length; i--;) {
          var node = nodes[i], nodeType = node.nodeType;
          if (nodeType == 3) {
            if (!opt_fnFilter || opt_fnFilter(node, elem)) {
              textNodes.push(node);
            }
          }
          else if (nodeType == 1 || nodeType == 9 || nodeType == 11) {
            textNodes = textNodes.concat(getTextNodesIn(node, opt_fnFilter));
          }
        }
      }
      return textNodes;
    }

    “有趣的事实”:在 ES6 表示法中,函数可以以更短的方式重写为:

    function getTN(elem, opt_flt) {
      if (elem) return [...elem.childNodes].reduce((tn,node)=>{
        var nty = node.nodeType;
        if (nty==3 && (!opt_flt || opt_flt(node, elem))) tn.push(node);
        else if (nty==1 || nty==9 || nty==11)            tn=tn.concat(getTN(node, opt_flt));
        return tn
      }, []);
    }
    

    【讨论】:

    • 我认为他希望替换所有“an”,而不仅仅是 spans 中的。
    • 是的,我的 sn-p 只会对 &lt;span&gt; 标签内的文本部分进行操作。
    【解决方案3】:

    您可以检查此解决方案。我已经从字符串中删除了所有 html 标记,然后应用了替换操作。它适用于您的两个测试用例。

    const regExFinder = new RegExp("an", 'gi');
    let sourceHTML = "<div> an <span> is an </span></div>";
    sourceHTML = sourceHTML.replace(/<[^>]*>?/gm, '').trim();  // removing HTML tags
    sourceHTML = sourceHTML.replace(regExFinder, 'a');
    
    console.log(sourceHTML)

    【讨论】: