如何将 DOM 关键字包装在 span 标签中

Posted

技术标签:

【中文标题】如何将 DOM 关键字包装在 span 标签中【英文标题】:How to wrap DOM keywords in a span tag 【发布时间】:2019-09-12 03:57:52 【问题描述】:

我正在编写一个 javascript 脚本,它将遍历 DOM 并将特定关键字包装在 <span> 标记中。我希望我的script 将任何出现的单词pan 包装在<span> 中,这样我就可以使用<span style='color: red' 对其进行样式设置。我其实不想用pan这个词,我只是用它作为例子。

我已经在这里查看了许多类似的帖子,但没有一个能解决我的问题。大多数要么是核心的、过于复杂和令人困惑的,要么是过于简单的,并且不能按我的预期工作。

这是我目前所写的:

<html>
  <body>
    <p>My <span style='font-weight: bold'>favorite</span> kitchen item is the pan.</p>
    <p>A pan can also be used as a weapon.</p>
    <script>
      // walk the document body
      function walk (node) 
        // if text node
        if (node.nodeType == 3) 
          // will always be an element
          const parent = node.parentNode;
          
          // ignore script and style tags
          const tagName = parent.tagName;
          if (tagName !== 'SCRIPT' && tagName !== 'STYLE') 
            
            // wrap occurrences of 'pan' in a red `<span>` tag
            const span = '<span style="color: red">pan</span>';
            parent.innerHTML = parent.innerHTML.replace (/pan/g, span)
          
        
        node = node.firstChild;
        while (node) 
          walk (node);
          node = node.nextSibling;
        
      
      walk (document.body)
    </script>
  </body>
</html>

此代码大部分时间都按预期运行。然而,在这个例子中,它没有。如果你要运行这段代码,this 将是结果。

我知道是什么原因造成的。但是,我不知道如何解决它。

两个文本节点Mykitchen item is the pan. 的父元素具有以下innerHTMLMy &lt;span style="font-weight: bold"&gt;favorite&lt;/span&gt; kitchen item is the pan. &lt;span&gt; 中的“pan”正在被替换,并导致了问题。

如果我使用parentNode.textContent 而不是parentNode.innerHTML,它不会将其包装在&lt;span&gt; 标记中,而是将其作为可见文本插入。

我知道这可以通过将/pan/g 更改为/\bpan\b/g 来解决,但这只能解决我创建的这个示例。我需要将 &lt;span&gt; 标记仅插入到文本内容中,而不是标记名称或其他 HTML。

我该怎么办?

【问题讨论】:

tagName !== 'script' || tagName !== 'style' 该条件将始终为true @ziggywiggy 哎呀,我会解决的。 @TylerRoper 已修复。谢谢!我知道出了什么问题,我只是快速编写这个示例来演示出了什么问题,而没有共享大量脚本。 您的问题解决了吗?或者你还需要一个答案! @ZeeshanAhmadKhalil 不,还没有解决。那只是一个小错误。 【参考方案1】:

使用转义的搜索字符串搜索给定的htmlString。这样做(通过适当的转义)将有助于避免匹配 HTML 标签(例如&lt;span&gt;)或子字符串(例如Pandora)等问题。

/*
highlight(selector, string)
@ Params:
  selector [String]: Same syntax as CSS/jQuery selector
  string   [String]: Seach string
*/
// A: Get the htmlString of the target's content
// B: Escape the search string
// C: Create a RegExp Object of the escaped search string
// D: Find and replace all matches with match wrapped in a <mark>
// E: Remove original HTML
// F: Insert new HTML
function highlight(selector, string) 
  let dom = document.querySelector(selector);
  let str = dom.innerHTML; //A
  let esc = `(?!(?:[^<]+>|[^>]+<\\/a>))\\b($string)\\b`; //B
  let rgx = new RegExp(esc, "gi"); //C
  let txt = str.replace(rgx, `<mark>$1</mark>`); //D
  dom.innerHTML = ''; //E 
  dom.insertAdjacentHTML('beforeend', txt); //F


highlight('body', "pan");
<html>

<body>
  <p>My <span style='font-weight: bold'>favorite</span> kitchen item is the pan.</p>
  <p>A pan can also be used as a weapon.</p>
  <p>Pan was the Greek god of the wild.</p>
  <p>Eboli was breifly a pandemic threat.</p>

</body>

</html>

【讨论】:

哇,实际上效果很好。谢谢!我对 RegEx 有点困惑,你介意详细说明一下吗?它让我想起了我写的一个类似的:/&lt;[^]*?&lt;\/[^]*?&gt;/g 嗨@KadenCampbell [^&lt;] 是一个排除&lt; 的任何数字+ 后跟&gt; OR | 排除类[^&gt;]+ 的类任意数量的&gt;,后跟链接结束标签&lt;\\/a&gt;。所有这些都包含在一个否定的前瞻 (?!...) 中,这意味着应该忽略 (?!...) 后面的任何内容。 \\b($string)\\b 将搜索字符串与\\b 边界元序列隔离开来,这些元序列指示非单词和单词的开始/结束。

以上是关于如何将 DOM 关键字包装在 span 标签中的主要内容,如果未能解决你的问题,请参考以下文章

防止为 ASP.NET 服务器控件包装 <span> 标记

AE软件中关键帧的几种类型及各自的作用?

棘手的 jQuery DOM 操作(包装文本)

JavaScript 10 DOM

如何获得连续的 nowrap 跨度以包装为单独的元素?

从Vue的DOM构建机制中理解key