По существу, я хочу, чтобы script выполнялся при изменении содержимого DIV. Поскольку сценарии являются отдельными (содержимое script в chrome extension и webpage script), мне нужен способ просто наблюдать изменения в состоянии DOM. Я мог настроить опрос, но это кажется неаккуратным.
Есть ли прослушиватель изменений JavaScript/jQuery DOM?
Ответ 1
Несколько лет спустя в настоящее время официально существует лучшее решение. DOM4 Mutation Observers заменяют устаревшие события мутации 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
и все его поддерево, и он будет активировать изменения атрибутов элемента, а также структурные изменения. В проекте spec есть полный список действительных свойств прослушивателя мутаций:
childList
- Установите для
true
, если необходимо наблюдать мутации для целевых детей.атрибуты
- Установите для
true
, если необходимо наблюдать мутации для целевых атрибутов.CharacterDataSTRONG >
- Установите для
true
, если необходимо отслеживать мутации для целевых данных.поддерево
- Установите для
true
, если необходимо учитывать мутации, а не целевые потомки.attributeOldValue
- Установите значение
true
, если для параметраattributes
установлено значение true и target, перед тем как мутация должна быть записана.characterDataOldValue
- Установите
true
, если для параметраcharacterData
установлено значение true и целевые данные до того, как необходимо записать мутацию.attributeFilter
- Установить список локальных имен атрибутов (без пространства имен), если не все атрибутные мутации необходимо соблюдать.
(Этот список действует с апреля 2014 года, вы можете проверить спецификацию на любые изменения.)
Ответ 2
редактировать
Этот ответ теперь устарел. См. Ответ apsillers.
Поскольку это расширение Chrome, вы можете также использовать стандартное событие DOM - DOMSubtreeModified
. См. Поддержку этого события в браузерах. Он поддерживается в Chrome с версии 1.0.
$("#someDiv").bind("DOMSubtreeModified", function() {
alert("tree changed");
});
См. Рабочий пример здесь.
Ответ 3
Многие сайты используют AJAX для динамического добавления/отображения/изменения контента. Иногда он используется вместо навигации на сайте, поэтому текущий URL-адрес изменяется программно, а сценарии содержимого в этом случае автоматически не выполняются браузером, так как страница не извлекается с удаленного сервера целиком.
Обычные JS-методы обнаружения изменений страницы, доступные в контенте script.
-
MutationObserver (docs), чтобы буквально обнаружить изменения DOM:
-
Слушатель событий для сайтов, которые сигнализируют изменение контента, отправив событие DOM:
-
pjax:end
ondocument
, используемый многими сайтами на основе pjax, например. GitHub,
см. Как запустить jQuery до и после загрузки pjax? -
message
наwindow
, используемом, например, Поиск Google в браузере Chrome,
см. Расширение Chrome обнаруживает обновление поиска Google -
spfdone
наdocument
, используемом Youtube,
см. Как определить навигацию по страницам на Youtube и изменить HTML до отображения страницы?
-
-
Периодическая проверка DOM с помощью setInterval:
Очевидно, что это будет работать только в тех случаях, когда вы ожидаете появления определенного элемента, идентифицированного его идентификатором/селектором, и он не позволит вам универсально обнаруживать новый динамически добавленный контент, если вы не изобрете какой-либо отпечаток существующего содержимого. -
Cloaking API истории внутри вложенный DOM script:
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 в background/страница событий.
Есть расширенный API для работы с навигацией: webNavigation, webRequest, но мы будем использовать простой chrome.tabs.onUpdated прослушиватель событий, который отправляет сообщение в контент script:
-
manifest.json:
declare фон/страница событий
объявить контент script
add"tabs"
permission. -
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(); } });
Ответ 4
Другой подход зависит от того, как вы меняете 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.
Ответ 5
В дополнение к "необработанным" инструментам, предоставляемым 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);
}