Более быстрый способ замены текста во всех элементах dom?

Я пытаюсь заменить весь текст между тегами и хочу узнать самый быстрый способ сделать это.

Примером может быть попытка заменить весь текст произвольной строкой helloWorld, так что это:

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

Становится так:

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

Мой текущий подход будет:

  • Выполнить поиск в глубину (DFS) в DOM
  • Для каждого элемента проанализируйте и определите, какая часть является текстом, а какая частью является элементом.
  • Для части, которая является текстом, замените его.

Для меня это было бы очень медленно, особенно если пытаться сделать это для большого документа и повторять процесс много раз. Есть ли более быстрый способ?


person Darkzuh    schedule 04.02.2017    source источник
comment
Используйте 1_.   -  person    schedule 04.02.2017
comment
Вы можете использовать концепцию всплытия в Js для этого подхода.   -  person Abhishek Agarwalla    schedule 04.02.2017
comment
Я дважды проверю и treeWalker, и nodeIterator. Правильно ли я предполагаю, что получу список всех узлов в порядке документа, поэтому мне не нужно выполнять обход? Если это так, я мог бы перебрать этот список и выполнить замену для каждого элемента.   -  person Darkzuh    schedule 04.02.2017


Ответы (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>

person jetpackpony    schedule 04.02.2017

Используйте TreeWalker как самый быстрый инструмент для обхода DOM.
Создать TreeWalker можно с помощью Document.createTreeWalker().

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

Демонстрация теста производительности

person RomanPerekhrest    schedule 04.02.2017

nodeIterator работает довольно быстро. У него нет проблем с вложенными узлами, независимо от того, насколько глубоко они похоронены. Примечание: добавлен красный текст на 6 уровней ниже. Подробности прокомментированы в 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>

person zer00ne    schedule 04.02.2017

Поиск DOM, выполняемый браузером, очень быстрый, и он также оптимизирован. Итак, я бы предложил добавить некоторый общий класс к элементам DOM, которые необходимо изменить, а затем манипулировать ими, используя этот идентификатор класса.

Также,

К вашему сведению, document.getElementById() работает в DFS и довольно эффективно.

person Vishal    schedule 04.02.2017
comment
Он не хочет выполнять поиск в DOM и не хочет изменять некоторые элементы. Он хочет работать со всеми текстовыми узлами. Зачем ему document.getElementByid? - person ; 04.02.2017
comment
Да, я не вижу смысла добавлять идентификаторы к каждому узлу. Я хочу пройти через дом, а затем изменить внутренний текст - person Darkzuh; 04.02.2017
comment
Я не говорю вам делать это с помощью document.getElementById(). Я предложил, чтобы браузер использовал только DFS, поэтому вы можете продолжить с DFS. (именно поэтому я поставил FYI). - person Vishal; 06.02.2017

Прокрутите свой HTML, затем найдите nodeValue следующим образом:

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

https://jsfiddle.net/q7ewbswx/

person romuleald    schedule 04.02.2017