Есть слушатель изменения DOM JavaScript / jQuery?

402

По сути, я хочу, чтобы скрипт выполнялся при изменении содержимого DIV. Поскольку сценарии являются отдельными (контентный скрипт в расширении Chrome и скрипте веб-страницы), мне нужен способ просто наблюдать изменения в состоянии DOM. Я мог бы настроить опрос, но это кажется неаккуратным.

Флетчер Мур
источник

Ответы:

488

В течение долгого времени события мутации DOM3 были наилучшим доступным решением, но они были признаны устаревшими по соображениям производительности. Наблюдатели мутации 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

  • Установите в значение trueif attributes, равное true, и в качестве значения атрибута цели перед записью мутации.

characterDataOldValue

  • Установите в значение trueif, если characterDataустановлено значение true, и данные цели перед записью мутации.

attributeFilter

  • Установите список локальных имен атрибутов (без пространства имен), если не все мутации атрибутов необходимо соблюдать.

(Этот список действителен по состоянию на апрель 2014 года; вы можете проверить спецификации на наличие изменений.)

apsillers
источник
3
@AshrafBashir Я вижу, что пример отлично работает в Firefox 19.0.2: я вижу ([{}])журнал в консоли, который показывает ожидаемое, MutationRecordкогда я нажимаю на него. Пожалуйста, проверьте еще раз, поскольку это может быть временная техническая ошибка в JSFiddle. Я еще не тестировал его в IE, поскольку у меня нет IE 10, который в настоящее время является единственной версией, поддерживающей события мутации.
Апсиллеры
1
Я только что опубликовал ответ, который работает в IE10 + и в основном во всем остальном.
naugtur
1
В спецификации, похоже, больше нет зеленого поля, в котором перечислены параметры наблюдателя мутации. Он перечисляет опции в разделе 5.3.1 и описывает их чуть ниже.
LS
2
@LS Спасибо, я обновил ссылку, удалил немного о зеленом поле и отредактировал весь список в своем ответе (на случай гниения будущей ссылки).
Апсиллеры
3
Вот таблица совместимости браузера от я могу использовать.
bdesham
211

редактировать

Этот ответ сейчас устарел. Смотрите ответ от апсиллеров .

Поскольку это расширение Chrome, вы можете также использовать стандартное событие DOM - DOMSubtreeModified. Смотрите поддержку этого события в разных браузерах. Поддерживается в Chrome с 1.0.

$("#someDiv").bind("DOMSubtreeModified", function() {
    alert("tree changed");
});

Смотрите рабочий пример здесь .

Анураг
источник
Я заметил, что это событие может быть запущено даже после определенных селекторов. Я сейчас расследую.
Педер Райс
9
w3.org/TR/DOM-Level-3-Events/#event-type-DOMSubtreeModified говорит, что это событие устарело, что бы мы использовали вместо этого?
Маслоу
2
@ Маслоу: нет! stackoverflow.com/questions/6659662/…
Сэм Рюби
4
Существует github.com/joelpurra/jquery-mutation-summary, который в основном решает ее для пользователей jquery.
Capi Etheriel
1
По-прежнему работает, если вы создаете расширения Chrome. бесценным.
concept47
49

Многие сайты используют 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();
        }
    });
wOxxOm
источник
29

Другой подход в зависимости от того, как вы меняете 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.

Зак Имбоден
источник
7

В дополнение к «необработанным» инструментам, предоставляемым MutationObserverAPI , существуют «удобные» библиотеки для работы с мутациями 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);
}
Xan
источник