在所有DOM元素中替换文本的更快方法?

7
我是一名有用的助手,可以为您进行文本翻译。以下是需要翻译的内容:

我想替换标签之间的所有文本,我想知道最快的方法。

例如,尝试将所有文本替换为任意字符串helloWorld,使其变为:

<div>
    <div>
        RandomText1
        <div>
            RandomText2
        </div>
    </div>
</div>

成为这样:

变成这个:

<div>
    <div>
        helloWorld
        <div>
            helloWorld
        </div>
    </div>
</div>

我目前的方法是:

  • 在DOM上进行深度优先搜索(DFS)
  • 对于每个元素,解析并确定哪部分是文本,哪部分是元素。
  • 对于文本部分进行替换。

对于一个大型文档,这种方法非常慢,尤其是需要多次重复该过程。有更快的方法吗?


1
使用 TreeWalker - user663031
或者 nodeIterator - zer00ne
你可以在Js中使用冒泡的概念来实现这种方法。 - Abhishek Agarwalla
我会仔细检查treeWalker和nodeIterator。如果我正确地假设我将按文档顺序收到所有节点的列表,那么我不必进行遍历,是吗?如果是这样,我可以迭代该列表并在每个元素上执行替换操作。 - Darkzuh
5个回答

5

您不需要解析每个元素来查找文本节点,您可以只需递归遍历元素的childNodes属性。

var newText = 'hello world';
function replaceTextNodes(node) {
  node.childNodes.forEach(function(el) {
    if (el.nodeType === 3) {  // If this is a text node, replace the text
      if (el.nodeValue.trim() !== "") { // Ignore this node it it an empty text node
        el.nodeValue = newText;
      }
    } else { // Else recurse on this node
      replaceTextNodes(el);
    }
  });
}

var onClick = replaceTextNodes.bind(null, document.querySelector('#container'));
document.querySelector('#replace').addEventListener('click', onClick);
<div id='container'>
  <div>
    RandomText1
    <div>
      RandomText2
      <ul>
        <li>RandomText3</li>
      </ul>
    </div>
  </div>
</div>
<button id="replace">Replace</button>


4

使用 TreeWalker 对象作为最快的 DOM 遍历工具。
可以使用 Document.createTreeWalker() 方法创建一个 TreeWalker

function replaceAllText(newText) {
    var walker = document.createTreeWalker(
        document.body,  // root node
        NodeFilter.SHOW_TEXT,  // filtering only text nodes
        null,
        false
    );
    
    while (walker.nextNode()) {
        if (walker.currentNode.nodeValue.trim())  // if it's not empty(whitespaced) node
          walker.currentNode.nodeValue = newText;
    }
}

replaceAllText("helloWorld");
<div>
    <div>
        RandomText1
        <div>
            RandomText2
        </div>
    </div>
</div>

https://developer.mozilla.org/zh-CN/docs/Web/API/Document/createTreeWalker

性能测试演示


3

nodeIterator非常快速。无论嵌套节点有多深,它都没有问题。注意:添加了6级红色文本。详细信息在代码片段中进行了注释。

代码片段

/* Create a custom filter which will...
||...the 3rd parameter of createNodeIterator method...
*/

function textFilter(node) {
  // if .nodeType is 3 (3 is text, 1 is element)
  if (node.nodeType === 3) {
    // Set .nodeValue to 'hellowWorld'
    node.nodeValue = 'helloWorld';
    // Return NodeFilter object to accept node
    return NodeFilter.FILTER_ACCEPT;
  }
  // Otherwise ignore node
  return NodeFilter.FILTER_SKIP;
}

function findText() {
  // Reference the rootNode
  var content = document.querySelector('body');

  /* Create nodeIterator passing
  || content or rootNode
  || NodeFilter object or WhatToShow property
  || Custom filter function
  */
  var iterator = document.createNodeIterator(content, NodeFilter.SHOW_TEXT, textFilter);
  // Advance to the next sibling or descend to node's children nodes 
  var node = iterator.nextNode();
  // While there is a node...
  while (node) {
    // ...Go on to it...rinse, lather, and repeat
    node = iterator.nextNode();
  }

}

findText();
.mark {
  color: red;
}
<div>
  <div>
    RandomText1
    <div>
      RandomText2
    </div>
  </div>
</div>
<div>
  <div>
    <div>
      <div>
        <div>
          <div class='mark'>
            6 Deep!
          </div>
        </div>
      </div>
    </div>
  </div>
  <div>
    RandomText1
    <div>
      RandomText2
    </div>
  </div>
</div>
<div>
  <div>
    RandomText1
    <div>
      RandomText2
    </div>
  </div>
</div>
<div>
  <div>
    RandomText1
    <div>
      RandomText2
    </div>
  </div>
</div>
<div>
  <div>
    RandomText1
    <div>
      RandomText2
    </div>
  </div>
</div>


0

浏览器执行的DOM搜索非常快,而且它也进行了优化。 因此,我建议在需要更改的DOM元素上添加一些公共类,然后使用该类标识符来操作它们。

另外,

顺便提一下,document.getElementById()是基于深度优先搜索算法实现的,效率相当高。


他不想进行“DOM搜索”,也不想更改某些元素。他想处理所有文本节点。那为什么他需要document.getElementByid呢? - user663031
是的,我不认为有必要给每个节点添加ID。我想遍历DOM,然后更改内部文本。 - Darkzuh
我并不是要让你使用document.getElementById()。我只是在建议浏览器只使用DFS,所以你可以继续使用DFS。(这就是为什么我加了FYI的原因)。 - Vishal

0
循环遍历你的HTML,然后像这样查找nodeValue:
document.querySelectorAll('div').forEach(function(o,i){
    console.log(o.firstChild && o.firstChild.nodeValue);
})

https://jsfiddle.net/q7ewbswx/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接