Сначала вы проверяете узлы #text
, чтобы увидеть, содержит ли текст слово, которое вы пытаетесь выделить, а затем выполняете замену на .innerHTML
родительского элемента. Есть несколько проблем с этим.
- Бесконечные замены: когда вы изменяете
.innerHTML
родительского элемента, вы изменяете массив childNodes
. Вы делаете это таким образом, что добавляете узел дальше в массив, содержащий текст, который нужно заменить. Таким образом, когда вы продолжаете сканирование массива childNodes
, вы всегда находите (новый) узел, содержащий текст, который вы хотите заменить. Итак, вы снова заменяете его, создавая другой узел с более высоким индексом в массиве childNodes
. Повторять бесконечно.
- Использование регулярного выражения для замены текста в свойстве
.innerHTML
. Хотя вы уже проверили, что текст, который вы хотите заменить, действительно содержится в текстовом узле, это не мешает вашему регулярному выражению также заменять любые совпадающие слова в фактическом HTML-элементе (например, в src="yourWord"
, href="http://foo.com/yourWord/bar.html"
или при попытке выделить такие слова, как style
, color
, background
, span
, id
, height
, width
, button
, form
, input
и т. д.).
- Вы не проверяете, не изменяете ли текст в тегах
<script>
или <style>
.
- You are checking that you only make changed in text nodes (i.e. you check for
node.nodeType === 3
). If you weren't checking for this you would also have the following possible problems due to using .innerHTML
to change HTML:
- You could end up changing attributes, or actual HTML tags, depending on what you are changing with
.replace()
. This could completely disrupt the page layout and functionality.
- Когда вы меняете
.innerHTML
, DOM для этой части страницы полностью воссоздается. Это означает, что элементы, в то время как новые элементы могут быть того же типа с теми же атрибутами, любые прослушиватели событий, которые были прикреплены к старым элементам, не будут присоединены к новым элементам. Это может существенно нарушить функциональность страницы.
- Многократное изменение больших частей DOM может потребовать больших вычислительных ресурсов для повторного рендеринга страницы. В зависимости от того, как вы это сделаете, вы можете столкнуться со значительными проблемами производительности, воспринимаемыми пользователями.
Таким образом, если вы собираетесь использовать RegExp для замены текста, вам нужно выполнить операцию только с содержимым узла #text
, а не с .innerHTML
родительского узла. Поскольку вы хотите создать дополнительные элементы HTML (например, новые элементы <span style="">
с дочерними узлами #text
), возникают некоторые сложности.
Невозможно назначить текст HTML текстовому узлу для создания новых узлов HTML:
Невозможно назначить новый HTML непосредственно текстовому узлу и оценить его как HTML, создав новые узлы. Присвоение свойству .innerHTML
текстового узла создаст такое свойство в Объекте (так же, как и в любом Объекте), но не изменит текст, отображаемый на экране (т. е. фактическое значение узла #text
). Таким образом, он не выполнит то, что вы хотите сделать: он не создаст никаких новых дочерних элементов HTML родительского узла.
Способ сделать это, который имеет наименьшее влияние на DOM страницы (т.е. с наименьшей вероятностью нарушить существующий JavaScript на странице), состоит в том, чтобы создать <span>
для включения новых текстовых узлов, которые вы создаете (текст, который был в узле #text
, который не находится в вашем цветном <span>
) вместе с потенциально несколькими элементами <span>
, которые вы создаете. Это приведет к замене одного узла #text
одним элементом <span>
. Хотя это создаст дополнительных потомков, количество дочерних элементов в родительском элементе останется неизменным. Таким образом, любой JavaScript, который полагался на это, не будет затронут. Учитывая, что мы меняем модель DOM, невозможно не сломать другой JavaScript, но это должно свести к минимуму эту возможность.
Некоторые примеры того, как вы можете это сделать: см. этот ответ (заменяет список слов этими словами в кнопках) и этот ответ (помещает весь текст в элементы <p>
, разделенные пробелами на кнопки) для полных расширений, выполняющих замену регулярных выражений новым HTML . См. этот ответ, который в основном делает то же самое, но создает ссылку (у него другая реализация, которая пересекает DOM с помощью TreeWalker для поиска #text
узлов вместо NodeIterator, как в двух других примерах).
Вот код, который выполнит замену, которую вы хотите, для каждого текстового узла в document.body
и создаст новый HTML, необходимый для того, чтобы style
отличался в части текста:
function handleTextNode(textNode) {
if(textNode.nodeName !== '#text'
|| textNode.parentNode.nodeName === 'SCRIPT'
|| textNode.parentNode.nodeName === 'STYLE'
) {
//Don't do anything except on text nodes, which are not children
// of <script> or <style>.
return;
}
let origText = textNode.textContent;
let newHtml=origText.replace(/(teste)/gi
,'<span style="background-color: yellow">$1</span>');
//Only change the DOM if we actually made a replacement in the text.
//Compare the strings, as it should be faster than a second RegExp operation and
// lets us use the RegExp in only one place for maintainability.
if( newHtml !== origText) {
let newSpan = document.createElement('span');
newSpan.innerHTML = newHtml;
textNode.parentNode.replaceChild(newSpan,textNode);
}
}
let textNodes = [];
//Create a NodeIterator to get the text nodes in the body of the document
let nodeIter = document.createNodeIterator(document.body,NodeFilter.SHOW_TEXT);
let currentNode;
//Add the text nodes found to the list of text nodes to process.
while(currentNode = nodeIter.nextNode()) {
textNodes.push(currentNode);
}
//Process each text node
textNodes.forEach(function(el){
handleTextNode(el);
});
Есть и другие способы сделать это. Однако они будут генерировать более значительные изменения в структуре дочерних элементов для этого конкретного элемента (например, несколько дополнительных узлов в родительском). Это с большей вероятностью сломает любой код JavaScript, уже находящийся на странице, который опирается на текущую структуру страницы. На самом деле, любое подобное изменение может сломать текущий JavaScript.
Код в этом ответе был изменен по сравнению с кодом в другом моем ответе
person
Makyen♦
schedule
21.11.2016
replacedText
? - person Scott Marcus   schedule 21.11.2016replacedText
, он показывает правильное значение, например,<span style=\"background-color: yellow\">teste</span>
. Однако, если я использую это наelement.innerHTML
, моя вкладка выходит из строя. - person rafaelcpalmeida   schedule 21.11.2016\"
? Вы пытались изменить строку на:"<span style='background-color: yellow'>$1</span>"
? - person Scott Marcus   schedule 21.11.2016<span style="background-color: yellow">Teste</span> de velocidade <strong>MEO</strong>
. Я изменил с"
на'
, и вкладка все равно вылетает. - person rafaelcpalmeida   schedule 21.11.2016