Есть слушатель изменения DOM JavaScript/jQuery?
По сути, я хочу, чтобы скрипт выполнялся при изменении содержимого DIV. Поскольку сценарии являются отдельными (контентный скрипт в расширении chrome и скрипт веб-страницы), мне нужен способ просто наблюдать изменения в состоянии DOM. Я мог бы настроить опрос, но это кажется неаккуратным.
5 ответов
Несколько лет спустя, теперь официально существует лучшее решение. Наблюдатели мутации DOM4 являются заменой устаревших событий мутации DOM3. В настоящее время они реализованы в современных браузерах как MutationObserver
(или как поставщик с префиксом WebKitMutationObserver
в старых версиях Chrome):
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
var observer = new MutationObserver(function(mutations, observer) {
// fired when a mutation occurs
console.log(mutations, observer);
// ...
});
// define what element should be observed by the observer
// and what types of mutations trigger the callback
observer.observe(document, {
subtree: true,
attributes: true
//...
});
Этот пример прослушивает изменения DOM на document
и все его поддерево, и оно будет запускать изменения атрибутов элемента, а также структурные изменения. Черновик спецификации имеет полный список допустимых свойств слушателя мутаций:
childList
- Установлен в
true
если нужно наблюдать мутации у детей-мишеней.атрибуты
- Установлен в
true
если должны наблюдаться мутации в атрибутах цели.CharacterData
- Установлен в
true
если должны наблюдаться мутации в данных цели.поддерево
- Установлен в
true
если мутации должны быть направлены не только на цель, но и на потомков цели.attributeOldValue
- Установлен в
true
еслиattributes
устанавливается в значение true и значение атрибута цели перед записью мутации.characterDataOldValue
- Установлен в
true
еслиcharacterData
устанавливается в true и данные цели перед записью мутации.attributeFilter
- Установите список локальных имен атрибутов (без пространства имен), если не все мутации атрибутов необходимо соблюдать.
(Этот список действителен по состоянию на апрель 2014 года; вы можете проверить спецификации на наличие изменений.)
редактировать
Этот ответ сейчас устарел. Смотрите ответ от апсиллеров.
Поскольку это расширение Chrome, вы можете также использовать стандартное событие DOM - DOMSubtreeModified
, Смотрите поддержку этого события в разных браузерах. Поддерживается в Chrome с 1.0.
$("#someDiv").bind("DOMSubtreeModified", function() {
alert("tree changed");
});
Смотрите рабочий пример здесь.
Многие сайты используют AJAX для динамического добавления / показа / изменения контента. Иногда он используется вместо навигации внутри сайта, поэтому текущий URL-адрес изменяется программно, и в этом случае браузер не выполняет автоматически скрипты содержимого, поскольку страница не извлекается полностью с удаленного сервера.
Обычные JS-методы обнаружения изменений страницы доступны в скрипте содержимого.
MutationObserver ( документы) для буквального обнаружения изменений DOM:
Прослушиватель событий для сайтов, которые сигнализируют об изменении содержимого, отправляя событие DOM:
pjax:end
наdocument
используется многими сайтами на основе pjax, например, GitHub,
смотрите Как запустить jQuery до и после загрузки pjax?message
наwindow
используется, например, поиском Google в браузере Chrome,
см. расширение Chrome обнаруживает обновление поиска Googlespfdone
наdocument
используется Youtube,
см. Как обнаружить навигацию по страницам на Youtube и изменить HTML перед отображением страницы?
Периодическая проверка DOM через setInterval:
Очевидно, что это будет работать только в тех случаях, когда вы ожидаете появления определенного элемента, идентифицируемого его идентификатором / селектором, и оно не позволит вам универсально обнаруживать новый динамически добавляемый контент, если вы не изобрели какой-либо способ снятия отпечатков пальцев с существующего содержимого.API Cloaking History внутри внедренного сценария DOM:
document.head.appendChild(document.createElement('script')).text = '(' + function() { // injected DOM script is not a content script anymore, // it can modify objects and functions of the page var _pushState = history.pushState; history.pushState = function(state, title, url) { _pushState.call(this, state, title, url); window.dispatchEvent(new CustomEvent('state-changed', {detail: state})); }; // repeat the above for replaceState too } + ')(); this.remove();'; // remove the DOM script element // And here content script listens to our DOM script custom events window.addEventListener('state-changed', function(e) { console.log('History state changed', e.detail, location.hash); doSomething(); });
Прослушивание hashchange, событий popstate:
window.addEventListener('hashchange', function(e) { console.log('URL hash changed', e); doSomething(); }); window.addEventListener('popstate', function(e) { console.log('State changed', e); doSomething(); });
Специфично для расширений: обнаружение изменений URL на странице фона / события.
Существует расширенный API для работы с навигацией: webNavigation, webRequest, но мы будем использовать простой приемник событий chrome.tabs.onUpdated, который отправляет сообщение в скрипт содержимого:
manifest.json:
объявить фон / страницу события
объявить содержание скрипта
добавлять"tabs"
разрешениеbackground.js
var rxLookfor = /^https?:\/\/(www\.)?google\.(com|\w\w(\.\w\w)?)\/.*?[?#&]q=/; chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { if (rxLookfor.test(changeInfo.url)) { chrome.tabs.sendMessage(tabId, 'url-update'); } });
content.js
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) { if (msg === 'url-update') { doSomething(); } });
Другой подход в зависимости от того, как вы меняете div. Если вы используете JQuery для изменения содержимого div с помощью его метода html(), вы можете расширить этот метод и вызывать функцию регистрации каждый раз, когда помещаете html в div.
(function( $, oldHtmlMethod ){
// Override the core html method in the jQuery object.
$.fn.html = function(){
// Execute the original HTML method using the
// augmented arguments collection.
var results = oldHtmlMethod.apply( this, arguments );
com.invisibility.elements.findAndRegisterElements(this);
return results;
};
})( jQuery, jQuery.fn.html );
Мы просто перехватываем вызовы html(), вызываем функцию регистрации с этим, которая в контексте относится к целевому элементу, получающему новое содержимое, затем мы передаем вызов исходной функции jquery.html (). Не забудьте вернуть результаты оригинального метода html(), потому что JQuery ожидает его для цепочки методов.
Для получения дополнительной информации о переопределении и расширении метода, проверьте http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm, где Я заколдовал функцию закрытия. Также ознакомьтесь с руководством по плагинам на сайте JQuery.
Помимо "сырых" инструментов, предоставляемых MutationObserver
API, существуют "удобные" библиотеки для работы с мутациями DOM.
Обратите внимание: MutationObserver представляет каждое изменение DOM с точки зрения поддеревьев. Так что, если вы, например, ожидаете вставки определенного элемента, он может быть глубоко внутри дочерних элементов mutations.mutation[i].addedNodes[j]
,
Другая проблема - когда ваш собственный код, в ответ на мутации, меняет DOM - вы часто хотите отфильтровать его.
Хорошая удобная библиотека, которая решает такие проблемы: mutation-summary
(отказ от ответственности: я не автор, я просто довольный пользователь), который позволяет вам задавать вопросы, которые вас интересуют, и получать именно это.
Базовый пример использования из документов:
var observer = new MutationSummary({
callback: updateWidgets,
queries: [{
element: '[data-widget]'
}]
});
function updateWidgets(summaries) {
var widgetSummary = summaries[0];
widgetSummary.added.forEach(buildNewWidget);
widgetSummary.removed.forEach(cleanupExistingWidget);
}