在保留现有元素和事件侦听器的同时修改HTML [英] Modify HTML while preserving the existing elements and event listeners

查看:45
本文介绍了在保留现有元素和事件侦听器的同时修改HTML的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新来的.我试图向您解释我的问题.我正在开发用于管理DOM的Chrome扩展程序.我必须在< p> 标签元素内拆分每个单词,以便在每个单词的某些CSS功能之后应用,但保留其他标签元素(< a>,< em>;,< strong> 等).

网页中可能的文本示例:

 < p>Sed ut< a>波斯菊属植物iste natus< em>错误坐</em>卷< strong>指责</strong>多洛伦克la</p> 

我已经考虑过使用jQuery,在每个单词周围放置一个< span> 标记,以定义要与css一起使用的类属性.我发现此代码可以正确分割单词(属于< p> ),但没有考虑< p> 中的其他可能元素.

使用的代码(不能满足我的需要):

 $("p").each(function(){var originalText = $(this).text().split('');var spannedText = [];对于(var i = 0; i< originalText.length; i + = 1){if(originalText [i]!="){spannedText [i] =('< span class ="...">'+ originalText.slice(i,i + 1).join('')+'</span>');;}}$(this).html(spannedText.join(''));}); 

在上面显示的示例中,此代码生成以下输出,除去其他标记元素:

 < p>< span> Sed</span>< span> ut</span>< span> perspiciatis</span>< span> unde</span>< span> omnis</span>< span> iste</span>< span> natus</span>< span>错误</span>< span>坐</span>< span>卷积</span>< span> Accusantium< span>< span> doloremque</span>< span>月桂酸</span></p> 

这与我需要的解决方案很接近,但是在这种情况下,示例中存在的所有标签(< a>,< em>,< strong> )都被删除并替换为< span> 标记.

相反,我想保留< p> 的html结构,并为每个单词仅插入< span> ...</span> ./p>

这是我想要实现的输出:

 < p>< span> Sed</span>< span> ut</span>< a>< span> perspiciatis</span>< span> unde</span>< span> omnis</span></a>< span> iste</span>< span> natus</span>< em>< span>错误</span>< span>坐</span></em>< span>卷积</span>< strong>< span> Accusantium< span></strong>< span> doloremque</span>< span>金刚烷</span></p> 

你能帮我吗?

解决方案

切勿通过innerHTML或jQuery的html()替换HTML

替换HTML会破坏JavaScript中添加到子元素的所有事件侦听器,并使浏览器重新解析整个过程,这是CPU密集型操作,因此在速度较慢的设备上运行速度可能很慢.不要这样做.

仅递归处理文本节点:

  const span = document.createElement('span');span.className ='foo';span.appendChild(document.createTextNode(''));//这些将显示< span>作为HTML规范的文字文本const skipTags = ['textarea','rp'];对于(document.getElementsByTagName('p')的const p){const walker = document.createTreeWalker(p,NodeFilter.SHOW_TEXT);//首先收集节点,因为我们不能在行走时插入新的跨度节点const textNodes = [];for(让n;(n = walker.nextNode());){if(n.nodeValue.trim()&&!skipTags.includes(n.parentNode.localName)){textNodes.push(n);}}对于(textNodes的const n){const fragment = document.createDocumentFragment();对于(n.nodeValue.split(/(\ s +)/)的const s){如果(s.trim()){span.firstChild.nodeValue = s;fragment.appendChild(span.cloneNode(true));} 别的 {fragment.appendChild(document.createTextNode(s));}}n.parentNode.replaceChild(fragment,n);}} 

由于我们可能要替换成千上万个节点,因此此代码试图变得更快:它使用 TreeWalker API,DOM克隆,通过简单的正则表达式 \ s + 和DocumentFragment跳过潜在的超长空格和换行符序列,以将新节点置于一个突变操作中.当然不使用jQuery.

P.S.有一些用于复杂得多的匹配和处理的高级库,例如 mark.js .

I'm new here. I try to explain you my problem. I'm developing an extension for Chrome that manage DOM. I have to split up each single word inside <p> tag element, to apply after some css features on each word, but preserving other tag elements (<a>, <em>, <strong>, etc.) that could be in <p> tag.

Example of possible text in a web page:

<p> 
   Sed ut <a> perspiciatis unde omnis </a> 
   iste natus <em> error sit </em> 
   voluptatem <strong> accusantium </strong> 
   doloremque laudantium 
</p>

Using jQuery, I've thought to put a <span> tag around each word to define a class attribute to use with css. I found this code that splits the words (belonging to <p>) correctly but doesn't consider other possible elements inside <p>.

Code used (that doesn't do what I need):


 $("p").each(function() {
    var originalText = $(this).text().split(' ');
    var spannedText = [];

    for (var i = 0; i < originalText.length; i += 1) {
        if(originalText[i] != ""){
           spannedText[i] = ('<span class="...">' + originalText.slice(i,i+1).join(' ') + '</span>');
         }
     }

     $(this).html(spannedText.join(' '));
 });

In the example shown above this codes generate the following output, removing the other tag elements:

<p> 
    <span>Sed</span> 
    <span>ut</span> 
    <span>perspiciatis</span> 
    <span>unde</span> 
    <span>omnis</span> 
    <span>iste</span> 
    <span>natus</span> 
    <span>error</span> 
    <span>sit</span> 
    <span>voluptatem</span> 
    <span>accusantium</span>
    <span>doloremque</span> 
    <span>laudantium</span> 
</p>

It is close to solution I need but in this case all the tags present in the example (<a>, <em>, <strong>) are removed and substituted with <span> tag.

Instead I want to keep the html structure of <p> and insert only <span>...</span> for each word.

This it the output I would like to achieve:

<p> 
    <span>Sed</span> 
    <span>ut</span> 
    <a> <span>perspiciatis</span> <span>unde</span> <span>omnis</span> </a>
    <span>iste</span> 
    <span>natus</span> 
    <em> <span>error</span> <span>sit</span> </em>
    <span>voluptatem</span> 
    <strong> <span>accusantium</span> </strong>
    <span>doloremque</span> 
    <span>laudantium</span> 
</p>

Can you help me?

解决方案

Never replace HTML via innerHTML or jQuery's html()

Replacing HTML destroys all event listeners added in JavaScript to the child elements and makes the browser re-parse the entire thing which is a CPU-intensive operation so it can be slow on slower devices. Don't do this.

Process only the text nodes recursively:

const span = document.createElement('span');
span.className = 'foo';
span.appendChild(document.createTextNode(''));

// these will display <span> as a literal text per HTML specification
const skipTags = ['textarea', 'rp'];

for (const p of document.getElementsByTagName('p')) {
  const walker = document.createTreeWalker(p, NodeFilter.SHOW_TEXT);
  // collect the nodes first because we can't insert new span nodes while walking
  const textNodes = [];
  for (let n; (n = walker.nextNode());) {
    if (n.nodeValue.trim() && !skipTags.includes(n.parentNode.localName)) {
      textNodes.push(n);
    }
  }
  for (const n of textNodes) {
    const fragment = document.createDocumentFragment();
    for (const s of n.nodeValue.split(/(\s+)/)) {
      if (s.trim()) {
        span.firstChild.nodeValue = s;
        fragment.appendChild(span.cloneNode(true));
      } else {
        fragment.appendChild(document.createTextNode(s));
      }
    }
    n.parentNode.replaceChild(fragment, n);
  }
}

Since we may be replacing thousands of nodes, this code tries to be fast: it uses TreeWalker API, DOM cloning, skipping the potentially superlong sequences of spaces and line breaks via a simple regular expression \s+, and DocumentFragment to place the new nodes in one mutation operation. And of course not using jQuery.

P.S. There are advanced libraries for much more complex matching and processing like mark.js.

这篇关于在保留现有元素和事件侦听器的同时修改HTML的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆