The docs on MDN имеет общий неполный пример и не демонстрируют распространенные ошибки. Mutation summary библиотека обеспечивает удобную для пользователя обертку, но, как и все обертки, она добавляет лишние накладные расходы. См. Performance of MutationObserver to detect nodes in entire DOM.
Создайте и запустите наблюдателя.
Давайте используем рекурсивный MutationObserver для документов, который сообщает все добавленные/удаленные узлы.
var observer = new MutationObserver(onMutation);
observer.observe(document, {
childList: true, // report added/removed nodes
subtree: true, // observe any descendant elements
});
Наивные перечисление добавленных узлов.
Замедляет загрузку чрезвычайно больших/сложных страниц, см. Performance.
Иногда пропускает элементы H1, объединенные в родительский контейнер, см. Следующий раздел.
function onMutation(mutations) {
mutations.forEach(mutation, mutation =>
Array.prototype
.filter.call(mutation.addedNodes, added =>
added.localName == 'h1' && added.textContent.match(/foo/)
).forEach(h1 =>
h1.innerHTML = h1.innerHTML.replace(/foo/, 'bar')
);
);
}
Эффективное перечисление добавленных узлов.
Теперь трудная часть. Узлы в записи мутации могут быть контейнерами во время загрузки страницы (например, весь блок заголовка сайта со всеми его элементами, указанными как только один добавленный узел): спецификация doesn't require каждый добавленный узел должен быть указан отдельно, поэтому нам придется загляните внутрь каждого элемента, используя querySelectorAll
(очень медленно) или getElementsByTagName
(очень быстро).
function onMutation(mutations) {
for (var i = 0, len = mutations.length; i < len; i++) {
var added = mutations[i].addedNodes;
for (var j = 0, lenAdded = added.length; j < lenAdded; j++) {
var node = added[j];
var found;
if (node.localName === 'h1') {
found = [node];
} else if (node.children && node.children.length) {
found = node.getElementsByTagName('h1');
} else {
continue;
}
for (var k = 0, lenFound = found.length; k < lenFound; k++) {
var h1 = found[k];
if (!h1.parentNode || !h1.textContent.match(/foo/)) {
// either removed in another mutation or has no text to replace
continue;
}
var walker = document.createTreeWalker(h1, NodeFilter.SHOW_TEXT);
while (walker.nextNode()) {
var textNode = walker.currentNode;
var text = textNode.nodeValue;
if (text.match(/foo/)) {
textNode.nodeValue = text.replace(/foo/, 'bar');
}
}
}
}
}
}
Почему уродливая ваниль for
петли? Потому что forEach
и filter
и ES2015 for (val of array)
очень медленны в сравнении. См. Performance of MutationObserver to detect nodes in entire DOM.
Почему TreeWalker? Чтобы сохранить прослушиватели событий, прикрепленные к подэлементам. Чтобы изменить только узлы Text
: у них нет дочерних узлов, и их изменение не вызывает новую мутацию, потому что мы использовали childList: true
, а не characterData: true
.
Обработка относительно редких элементов с помощью живых HTMLCollection без перечисления мутаций.
Итак, мы ищем элемент, который должен использоваться редко, как тег H1, или IFRAME и т. Д. В этом случае мы можем упростить и ускорить обратный вызов наблюдателя с автоматически обновляемым HTMLCollection, возвращаемым getElementsByTagName.
var h1s = document.getElementsByTagName('h1');
function onMutation(mutations) {
if (mutations.length == 1) {
// optimize the most frequent scenario: one element is added/removed
var added = mutations[0].addedNodes[0];
if (!added || (added.localName !== 'h1' && !added.children.length)) {
// so nothing was added or non-H1 with no child elements
return;
}
}
// H1 is supposed to be used rarely so there'll be just a few elements
for (var i = 0, len = h1s.length; i < len; i++) {
var h1 = h1s[i];
if (!h1.textContent.match(/foo/)) {
continue;
}
var walker = document.createTreeWalker(h1, NodeFilter.SHOW_TEXT);
while (walker.nextNode()) {
var textNode = walker.currentNode;
var text = textNode.nodeValue;
if (text.match(/foo/)) {
textNode.nodeValue = text.replace(/foo/, 'bar');
}
}
}
}
Hello @wOxxOm - Во-первых, извинения за то, что вы вызвали волнение в воскресенье, но спасибо за ваш ответ. Во-вторых, не могли бы вы поделиться тем, что конкретно вас беспокоит? В-третьих, по шкале от одного до десяти, как вы волновались? И, наконец, если у вас есть подходящий ресурс (ы) из одной из тонны легкодоступных примеров, возможно, вы могли бы поделиться им как с ответом, и если это будет правильно, я буду отмечать его так. Спасибо за вашу помощь. –
Мне было бы интересно услышать ответ на этот вопрос, я смотрел на это не так давно, но в итоге остановил его, так как у него не было времени или необходимости его завершить. – osouthgate
Спасибо за отзыв @wOxxOm - очень полезно, хотя ни одна из ссылок на первых двух страницах не предоставила действительного ответа, однако есть хорошие статьи. Если вам известно о дублированном потоке в stackoverflow, мы можем связать этот вопрос с этим как действительный ответ, если он считается допустимым дубликатом.Извините за то, что тратил свое время, но вы можете прекратить отвечать и, возможно, сосредоточить свои усилия на других. Я желаю вам здоровья. –