Обработать текст страницы
Есть задача, изменить все буквы(слова) А на Б на всей html загруженной страницы, из-под custom script
Самое банальное, что приходит в голову(не думая):
let x = $('body').html();
x = x.split('A').join('B');
$('body').html(x);
Ну, или тоже самое, но перебором элементов...
Дак вот, суть вопроса, как лучше это сделать - со стороны оптимизации и\или работы с большими строками..?
Т.е. банально регексп нарисовать и_к_нему\или переборщик элементов $('[value],:contains("A")')... например, или есть варианты полудшэ-е?
Суть не в том, чтобы не изменять например - имя класса, а в том, как со стороны оптимизации сделать всё гораздо быстрее и смышленнее чем перебором\работой со строкой, может есть какие-то ТРЮКИ\идеи, а не эти палка с камнем
Ответы (2 шт):
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>
Если я не ошибаюсь, в данной задаче эффективность не единственное, что нужно принять во внимание. Задумайтесь ещё и над такими моментами.
- Может ли замена испортить разметку и функциональность. В вашем варианте замена затронет всё — имена тегов и атрибуты, тексты стилей и скриптов.
- Полная перезапись всего 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');
}