最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Faster way of replacing text in all dom elements? - Stack Overflow

programmeradmin5浏览0评论

I'm trying to replace all text in between tags and I want to know the fastest way of doing so.

An example would be trying to replace all text with the arbitrary string helloWorld, so that this:

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

Bees this:

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

My current approach would be :

  • Do Depth-first search (DFS) on DOM
  • For each element parse and determine which part is text and which part is an element.
  • For the part that is text replace it.

This to me would be really slow, especially trying to do this for a large document and having to repeat the process many times. Is there a faster way?

I'm trying to replace all text in between tags and I want to know the fastest way of doing so.

An example would be trying to replace all text with the arbitrary string helloWorld, so that this:

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

Bees this:

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

My current approach would be :

  • Do Depth-first search (DFS) on DOM
  • For each element parse and determine which part is text and which part is an element.
  • For the part that is text replace it.

This to me would be really slow, especially trying to do this for a large document and having to repeat the process many times. Is there a faster way?

Share Improve this question edited Feb 4, 2017 at 13:11 mplungjan 178k28 gold badges181 silver badges240 bronze badges asked Feb 4, 2017 at 13:06 DarkzuhDarkzuh 2494 silver badges11 bronze badges 4
  • 1 Use TreeWalker. – user663031 Commented Feb 4, 2017 at 13:24
  • Or nodeIterator – zer00ne Commented Feb 4, 2017 at 13:32
  • You can use the concept of bubbling in Js for this approach – Abhishek Agarwalla Commented Feb 4, 2017 at 13:32
  • I'll double check both treeWalker and nodeIterator. Am I correct in assuming I'll receive back a list of all the nodes in document order, so I don't have to do the traversing? If so I could iterate through that list and do my replacement thing on each element – Darkzuh Commented Feb 4, 2017 at 13:38
Add a ment  | 

5 Answers 5

Reset to default 5

You don't need to parse each element to find text nodes, you can just recursively traverse childNodes property of an element

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>

Use TreeWalker object as the most fast tool for DOM traversal.
A TreeWalker can be created using the Document.createTreeWalker() method.

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/en-US/docs/Web/API/Document/createTreeWalker

Performance test demonstration

The nodeIterator is pretty fast. It has no problems with nested nodes no matter how deeply they're buried. Note: added red text that's 6 levels down. Details mented in Snippet.

SNIPPET

/* 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>

Loop over your HTML than find nodeValue like this:

document.querySelectorAll('div').forEach(function(o,i){
    console.log(o.firstChild && o.firstChild.nodeValue);
})

https://jsfiddle/q7ewbswx/

DOM searching done by the browser is very fast, and it optimized also. So, I would suggest to add some mon class on the DOM elements which need to be changed and then manipulate them using that class identifier.

Also,

FYI, document.getElementById() works on DFS and is pretty efficient.

发布评论

评论列表(0)

  1. 暂无评论