¿Hay un detector de cambio de DOM de JavaScript / jQuery?

402

Esencialmente quiero que se ejecute un script cuando el contenido de un DIVcambio. Como las secuencias de comandos están separadas (secuencia de comandos de contenido en la extensión de Chrome y secuencia de comandos de la página web), necesito una forma de simplemente observar los cambios en el estado DOM. Podría establecer encuestas, pero eso parece descuidado.

Fletcher Moore
fuente

Respuestas:

488

Durante mucho tiempo, los eventos de mutación DOM3 fueron la mejor solución disponible, pero han quedado en desuso por razones de rendimiento. Los observadores de mutación DOM4 son el reemplazo de los eventos de mutación DOM3 en desuso. Están implementados actualmente en los navegadores modernos como MutationObserver(o como el proveedor prefijada WebKitMutationObserveren las versiones anteriores de 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
  //...
});

Este ejemplo escucha los cambios DOM documenty todo su subárbol, y disparará los cambios a los atributos del elemento, así como los cambios estructurales. El borrador de la especificación tiene una lista completa de propiedades de escucha de mutación válidas :

childList

  • Establezca truesi se deben observar mutaciones en los hijos del objetivo.

atributos

  • Establezca truesi se deben observar mutaciones en los atributos del objetivo.

characterData

  • Establezca truesi se van a observar mutaciones en los datos del objetivo.

subárbol

  • Establezca truesi las mutaciones no solo apuntan, sino también a los descendientes del objetivo.

attributeOldValue

  • Establecer en truesi attributesse establece en verdadero y el valor del atributo del objetivo antes de que la mutación deba registrarse.

characterDataOldValue

  • Establecer en truesi characterDatase establece en verdadero y los datos del objetivo antes de que la mutación deba registrarse.

attributeFilter

  • Establezca una lista de nombres locales de atributos (sin espacio de nombres) si no es necesario observar todas las mutaciones de atributos.

(Esta lista está actualizada a abril de 2014; puede consultar la especificación para ver si hay cambios).

apsillers
fuente
3
@AshrafBashir Veo que la muestra funciona bien en Firefox 19.0.2: veo ([{}])registrada en la consola, que muestra lo esperado MutationRecordcuando hago clic en ella. Vuelva a verificar, ya que podría haber sido una falla técnica temporal en JSFiddle. Todavía no lo he probado en IE, ya que no tengo IE 10, que actualmente es la única versión que admite eventos de mutación.
apsillers
1
Acabo de publicar una respuesta que funciona en IE10 + y sobre todo en cualquier otra cosa.
naugtur
1
La especificación ya no parece tener un cuadro verde que enumere las opciones del observador de mutaciones. Lo hace la lista de las opciones en la sección 5.3.1 y los describe sólo un poco más debajo de eso.
LS
2
@LS Gracias, actualicé el enlace, eliminé el bit sobre el cuadro verde y edité la lista completa en mi respuesta (en caso de que el enlace se pudra en el futuro).
apsillers
211

Editar

Esta respuesta ahora está en desuso. Ver la respuesta de apsillers .

Dado que esto es para una extensión de Chrome, también puede usar el evento DOM estándar DOMSubtreeModified. Consulte el soporte para este evento en todos los navegadores. Ha sido compatible con Chrome desde 1.0.

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

Vea un ejemplo de trabajo aquí .

Anurag
fuente
Me di cuenta de que este evento se puede disparar incluso después de ciertos selectores. Actualmente estoy investigando.
Peder Rice
99
w3.org/TR/DOM-Level-3-Events/#event-type-DOMSubtreeModified dice que este evento está en desuso, ¿qué usaríamos en su lugar?
Maslow
2
@ Maslow- No hay! stackoverflow.com/questions/6659662/…
Sam Rueby
44
Hay github.com/joelpurra/jquery-mutation-summary que básicamente lo resuelve para los usuarios de jquery.
Capi Etheriel
1
Todavía funciona si estás creando extensiones de Chrome. inestimable.
concept47
49

Muchos sitios usan AJAX para agregar / mostrar / cambiar contenido dinámicamente. A veces se usa en lugar de la navegación en el sitio, por lo que la URL actual se cambia mediante programación y los scripts de contenido no se ejecutan automáticamente por el navegador en este caso, ya que la página no se obtiene del servidor remoto por completo.


Métodos habituales de JS para detectar cambios de página disponibles en un script de contenido .


Extensiones específicas: detecta cambios de URL en una página de fondo / evento .

Existen API avanzadas para trabajar con la navegación: webNavigation , webRequest , pero usaremos un detector de eventos simple chrome.tabs.onUpdated que envía un mensaje al script de contenido:

  • manifest.json:
    declarar página de fondo / evento
    declarar contenido script
    agregar "tabs" permiso .

  • 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
fuente
29

Otro enfoque dependiendo de cómo esté cambiando el div. Si está utilizando JQuery para cambiar el contenido de un div con su método html (), puede extender ese método y llamar a una función de registro cada vez que coloque html en un 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 );

Simplemente interceptamos las llamadas a html (), llamamos a una función de registro con esto, que en el contexto se refiere al elemento objetivo que obtiene nuevo contenido, luego pasamos la llamada a la función jquery.html () original. Recuerde devolver los resultados del método html () original, porque JQuery lo espera para el encadenamiento de métodos.

Para obtener más información sobre la anulación y extensión de métodos, consulte http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm , que es donde Puse la función de cierre. Consulte también el tutorial sobre complementos en el sitio de JQuery.

Zac Imboden
fuente
7

Además de las herramientas "en bruto" proporcionadas por MutationObserverAPI , existen bibliotecas de "conveniencia" para trabajar con mutaciones DOM.

Considere: MutationObserver representa cada cambio de DOM en términos de subárboles. Entonces, si está esperando, por ejemplo, que se inserte un determinado elemento, puede estar profundamente dentro de los hijos demutations.mutation[i].addedNodes[j] .

Otro problema es cuando su propio código, en reacción a las mutaciones, cambia el DOM; a menudo desea filtrarlo.

Una buena biblioteca de conveniencia que resuelve tales problemas es mutation-summary (descargo de responsabilidad: no soy el autor, solo un usuario satisfecho), que le permite especificar consultas de lo que le interesa y obtener exactamente eso.

Ejemplo de uso básico de los documentos:

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
fuente