Обработать текст страницы

Есть задача, изменить все буквы(слова) А на Б на всей html загруженной страницы, из-под custom script

Самое банальное, что приходит в голову(не думая):

let x = $('body').html();
x = x.split('A').join('B');
$('body').html(x);

Ну, или тоже самое, но перебором элементов...

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

Т.е. банально регексп нарисовать и_к_нему\или переборщик элементов $('[value],:contains("A")')... например, или есть варианты полудшэ-е?

Суть не в том, чтобы не изменять например - имя класса, а в том, как со стороны оптимизации сделать всё гораздо быстрее и смышленнее чем перебором\работой со строкой, может есть какие-то ТРЮКИ\идеи, а не эти палка с камнем


Ответы (2 шт):

Автор решения: OPTIMUS PRIME

let textNodes = (
  [...document.querySelectorAll("body, body *")] // body, и все HTML-элементы внутри него
  .map(e => [...e.childNodes])         // подменить их на их дочерние узлы
  .flat()
  .filter(node => node.nodeType === 3) // взять только текстовые
);

textNodes.forEach(txt => txt.textContent = txt.textContent.replace(/A/g, "B"));
.MAMA { color: red; }
<body>
  AAA
  <div class="MAMA">
    MAMA
    <div>EEE AAA</div>  
    LALA
  </div>
  AAA
</body>

→ Ссылка
Автор решения: vsemozhetbyt

Если я не ошибаюсь, в данной задаче эффективность не единственное, что нужно принять во внимание. Задумайтесь ещё и над такими моментами.

  1. Может ли замена испортить разметку и функциональность. В вашем варианте замена затронет всё — имена тегов и атрибуты, тексты стилей и скриптов.
  2. Полная перезапись всего html полностью заменит все DOM элементы. Если в head были какие-то скрипты и они повесили на элементы обработчики событий, всё это будет утрачено.

Если вам нужно наиболее аккуратно пройтись только по текстовым узлам и совершить замену в них, единственный вариант — выбирать эти текстовые узлы и проходиться по ним, поэтому от работы с DOM и от цикла с заменой вы вряд ли сможете уклониться. Один из вариантов — использовать XPath, с ним легче вычленить только текстовые узлы:

const xpathTextNodes = './descendant-or-self::*[not(self::script or self::style)]/text()';

const textNodes = document.evaluate(
  xpathTextNodes, document.body, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
);

for (let i = 0, node; (node = textNodes.snapshotItem(i)); i++) {
  node.textContent = node.textContent.replace(/A/gu, 'B');
}
→ Ссылка