#javascript
#javascript
Вопрос:
У меня есть HTML-документ со строкой, которую я хочу заменить. Вот строки ( 9673
):
<meta property="og:description" content="***April amp;amp;nbsp;9673amp;amp;nbsp;" />
<span class="agent-phone-number"> 5 9673</span>
<a href="#shownumber" class="js-agent-phone-number"><span class="agent-phone-number" data-mobile="9673">9673</span></a>
<span class="agent-phone-number"> 5 9673</span>
<li><a href="sms:9673" class="js-agent-phone-number-sms"><i class="pgicon pgicon-comment-o"></i>SMS</a></li>
После загрузки документа я хочу ввести Javascript для замены строки 9673
на 0000
. Как мне это сделать? Мне нужна только часть Javascript, где она выполняет поиск по всему документу HTMLL и заменяет строку.
Комментарии:
1. Вы спрашиваете, как «внедрить JavaScript» — или как заменить строковое значение в каждом значении атрибута и
#text
узле — или в обоих?2. Не спрашивая, как внедрить javascript. Позвольте мне отредактировать мой вопрос.
3. вы говорите о разборе html, верно?
Ответ №1:
Быстрый в написании и медленный в выполнении ответ — злоупотребление innerHTML
:
function replace_9673_everywhere() {
const documentHtml = document.documentElement.innerHTML;
const newDocumentHtml = documentHtml.replace( /9673/g, '0000' );
document.documentElement.innerHTML = newDocumentHtml;
}
…но это, вероятно, приведет к разрыву документа, потому что это заставит браузер повторно проанализировать все (поэтому любое текущее состояние сценария страницы со ссылками на элементы DOM будет нарушено).
Лучшим подходом является прохождение DOM, поскольку это не вызовет повторный анализ при условии, что вы управляете Element.attributes
и Element.textContent
(и #text
узлами по отдельности) и избегаете использования innerHTML
/ outerHTML
полностью:
function replace_9673_everywhere() {
const rootNode = document.documentElement;
replace_9673_in_element( rootNode );
}
function replace_9673_in_element( el ) {
// Replace in attributes:
for( let i = 0; i < el.attributes.length; i ) {
const attr = el.attributes.item(i);
if( attr.value.includes( '9673' ) ) {
attr.value = attr.value.replace( /9673/g, '0000' );
}
}
// Replace in child nodes (not just Element children):
for( const childNode of el.childNodes ) {
if( childNode.nodeType === Node.TEXT_NODE ) {
if( childNode.textContent.includes( '9673' ) ) {
childNode.textContent = childNode.textContent.replace( /9673/g, '0000' );
}
}
else if( childNode.nodeType === Node.ELEMENT_NODE ) {
replace_9673_in_element( childNode );
}
}
}
Комментарии:
1. Вы можете пропустить регулярное выражение и выполнить простую замену All()
2. Поддержка браузера @ShubhamSrivastava для
String.prototype.replaceAll
по-прежнему отсутствует: caniuse.com/?search=replaceAll (по состоянию на сентябрь 2020 года он поддерживается только браузерами Firefox 77 , Chromium 85 (который был выпущен всего пару недель назад) и Safari 13 , исключая текущие версии Opera и Android Firefox.3.
childNode.nodeType
всегда будет число, а не строка, поэтому сравнения всегда будут оцениваться какfalse
. Вместо этого вы можете использовать значения перечисления в разделеNode
:childNode.nodeType === Node.TEXT_NODE
иchildNode.nodeType === Node.ELEMENT_NODE
4. @LionelRowe Сумасшедший, спасибо! Извините, было 3 часа ночи, когда я написал свой ответ.
Ответ №2:
Вот другой подход, использующий TreeWalker
s, который нацелен только на текстовые узлы и атрибуты, не нарушая фактическую структуру HTML.
function* iterateNodes(walker) {
let next
while (next = walker.nextNode()) {
yield next
}
}
const nodesUnder = (el, nodeFilters) => {
const walker = document
.createTreeWalker(el, nodeFilters)
const nodes = iterateNodes(walker)
return [...nodes]
}
const htmlEl = document.documentElement
const textNodes = nodesUnder(htmlEl, NodeFilter.SHOW_TEXT)
const attrNodes = nodesUnder(htmlEl, NodeFilter.SHOW_ELEMENT)
.map(el => [...el.attributes])
.flat()
;[...textNodes, ...attrNodes].forEach(node => {
node.nodeValue = node.nodeValue.replace(/9673/g, '0000')
})
console.log(document.querySelector('#outer').outerHTML)
* { font-family: sans-serif }
<div id="outer">
<div id="changed-9673" data-attr="9673">9673 (changed)</div>
<custom-element-9673>unchanged</custom-element-9673>
</div>
Комментарии:
1. Хороший подход ( 1) — но я чувствую, что использование оператора распространения
[...nodes]
с вашей пользовательской функцией генератораiterateNodes
сводит на нет смысл использования функции генератора с ленивой оценкой, поскольку это сразу загрузит все ссылки на элементы в память. Почему бы просто неreturn nodes
внутриnodesUnder
?2. Идея заключается в том, что его можно использовать лениво, но вариант использования OP этого не требует. Возможно, это немного переработано для варианта использования, если честно.
Ответ №3:
Вы хотите что-то вроде этого
function replace9673() {
$("html").html($("html").html().replaceAll("9673", "0000"));
}
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<meta property="og:description" content="***April amp;amp;nbsp;9673amp;amp;nbsp;" />
</head>
<body>
<span class="agent-phone-number"> 5 9673</span>
<a href="#shownumber" class="js-agent-phone-number"><span class="agent-phone-number" data-mobile="9673">9673</span></a>
<span class="agent-phone-number"> 5 9673</span>
<li><a href="sms:9673" class="js-agent-phone-number-sms"><i class="pgicon pgicon-comment-o"></i>SMS</a></li>
<button onclick="replace9673()">Click Me</button>
</body>
</html>