Есть слушатель изменения 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-методы обнаружения изменений страницы доступны в скрипте содержимого.


Специфично для расширений: обнаружение изменений 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);
}
Другие вопросы по тегам