380 Stimmen

Änderungen im DOM erkennen

Ich möchte eine Funktion ausführen, wenn ein div oder eine Eingabe zur html-Datei hinzugefügt wird. Ist dies möglich?

Wird zum Beispiel eine Texteingabe hinzugefügt, so sollte die Funktion aufgerufen werden.

310voto

vsync Punkte 101339

Der bisher ultimative Ansatz mit dem kleinsten Code:

(IE11+, FF, Webkit)

Verwendung von MutationObserver und den Rückgriff auf die veraltete Mutationsereignisse falls erforderlich:
(Beispiel unten, wenn nur für DOM-Änderungen in Bezug auf hinzugefügte oder entfernte Knoten)

var observeDOM = (function(){
  var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

  return function( obj, callback ){
    if( !obj || obj.nodeType !== 1 ) return; 

    if( MutationObserver ){
      // define a new observer
      var mutationObserver = new MutationObserver(callback)

      // have the observer observe foo for changes in children
      mutationObserver.observe( obj, { childList:true, subtree:true })
      return mutationObserver
    }

    // browser support fallback
    else if( window.addEventListener ){
      obj.addEventListener('DOMNodeInserted', callback, false)
      obj.addEventListener('DOMNodeRemoved', callback, false)
    }
  }
})()

//------------< DEMO BELOW >----------------

// add item
var itemHTML = "<li><button>list item (click to delete)</button></li>",
    listElm = document.querySelector('ol');

document.querySelector('body > button').onclick = function(e){
  listElm.insertAdjacentHTML("beforeend", itemHTML);
}

// delete item
listElm.onclick = function(e){
  if( e.target.nodeName == "BUTTON" )
    e.target.parentNode.parentNode.removeChild(e.target.parentNode);
}

// Observe a specific DOM element:
observeDOM( listElm, function(m){ 
   var addedNodes = [], removedNodes = [];

   m.forEach(record => record.addedNodes.length & addedNodes.push(...record.addedNodes))

   m.forEach(record => record.removedNodes.length & removedNodes.push(...record.removedNodes))

  console.clear();
  console.log('Added:', addedNodes, 'Removed:', removedNodes);
});

// Insert 3 DOM nodes at once after 3 seconds
setTimeout(function(){
   listElm.removeChild(listElm.lastElementChild);
   listElm.insertAdjacentHTML("beforeend", Array(4).join(itemHTML));
}, 3000);

<button>Add Item</button>
<ol>
  <li><button>list item (click to delete)</button></li>
  <li><button>list item (click to delete)</button></li>
  <li><button>list item (click to delete)</button></li>
  <li><button>list item (click to delete)</button></li>
  <li><em>&hellip;More will be added after 3 seconds&hellip;</em></li>
</ol>

255voto

gblazex Punkte 47453

Update 2015, neu MutationObserver wird von modernen Browsern unterstützt:

Chrome 18+, Firefox 14+, IE 11+, Safari 6+

Wenn Sie ältere Menschen unterstützen müssen, können Sie auf andere Ansätze zurückgreifen, wie die in diesem Artikel genannten 5 (!) Jahre alte Antwort unten. Es lebe der Drache. Viel Spaß :)


Jemand anderes ändert das Dokument? Denn wenn Sie die volle Kontrolle über die Änderungen haben, müssen Sie nur Ihr eigenes Dokument erstellen. domChanged API - mit einer Funktion oder einem benutzerdefinierten Ereignis - und lösen Sie sie überall dort aus, wo Sie Dinge ändern.

En DOM Level-2 hat Arten von Mutationsereignissen , aber ältere Versionen des IE unterstützen es nicht. Beachten Sie, dass die Mutationsereignisse veraltet in der DOM3 Events Spezifikation und haben eine Leistungseinbuße .

Sie können versuchen, das Mutationsereignis zu emulieren mit onpropertychange in IE (und greifen auf den Brute-Force-Ansatz zurück, wenn keiner von ihnen verfügbar ist).

Für eine vollständig domChange ein Intervall könnte ein Overkill sein. Stellen Sie sich vor, Sie müssen den aktuellen Zustand des gesamten Dokuments speichern und die Eigenschaften jedes Elements überprüfen, damit sie gleich bleiben.

Wenn Sie nur an den Elementen und ihrer Reihenfolge interessiert sind (wie Sie in Ihrer Frage erwähnt haben), könnte eine getElementsByTagName("*") funktionieren kann. Dies wird automatisch ausgelöst, wenn Sie ein Element hinzufügen, ein Element entfernen, Elemente ersetzen oder die Struktur des Dokuments ändern.

Ich habe eine Machbarkeitsstudie geschrieben:

(function (window) {
    var last = +new Date();
    var delay = 100; // default delay

    // Manage event queue
    var stack = [];

    function callback() {
        var now = +new Date();
        if (now - last > delay) {
            for (var i = 0; i < stack.length; i++) {
                stack[i]();
            }
            last = now;
        }
    }

    // Public interface
    var onDomChange = function (fn, newdelay) {
        if (newdelay) delay = newdelay;
        stack.push(fn);
    };

    // Naive approach for compatibility
    function naive() {

        var last = document.getElementsByTagName('*');
        var lastlen = last.length;
        var timer = setTimeout(function check() {

            // get current state of the document
            var current = document.getElementsByTagName('*');
            var len = current.length;

            // if the length is different
            // it's fairly obvious
            if (len != lastlen) {
                // just make sure the loop finishes early
                last = [];
            }

            // go check every element in order
            for (var i = 0; i < len; i++) {
                if (current[i] !== last[i]) {
                    callback();
                    last = current;
                    lastlen = len;
                    break;
                }
            }

            // over, and over, and over again
            setTimeout(check, delay);

        }, delay);
    }

    //
    //  Check for mutation events support
    //

    var support = {};

    var el = document.documentElement;
    var remain = 3;

    // callback for the tests
    function decide() {
        if (support.DOMNodeInserted) {
            window.addEventListener("DOMContentLoaded", function () {
                if (support.DOMSubtreeModified) { // for FF 3+, Chrome
                    el.addEventListener('DOMSubtreeModified', callback, false);
                } else { // for FF 2, Safari, Opera 9.6+
                    el.addEventListener('DOMNodeInserted', callback, false);
                    el.addEventListener('DOMNodeRemoved', callback, false);
                }
            }, false);
        } else if (document.onpropertychange) { // for IE 5.5+
            document.onpropertychange = callback;
        } else { // fallback
            naive();
        }
    }

    // checks a particular event
    function test(event) {
        el.addEventListener(event, function fn() {
            support[event] = true;
            el.removeEventListener(event, fn, false);
            if (--remain === 0) decide();
        }, false);
    }

    // attach test events
    if (window.addEventListener) {
        test('DOMSubtreeModified');
        test('DOMNodeInserted');
        test('DOMNodeRemoved');
    } else {
        decide();
    }

    // do the dummy test
    var dummy = document.createElement("div");
    el.appendChild(dummy);
    el.removeChild(dummy);

    // expose
    window.onDomChange = onDomChange;
})(window);

Verwendung:

onDomChange(function(){ 
    alert("The Times They Are a-Changin'");
});

Dies funktioniert mit IE 5.5+, FF 2+, Chrome, Safari 3+ und Opera 9.6+

48voto

Anthony Awuley Punkte 2805

Das folgende Beispiel wurde von Mozilla Hacks' angepasst Blogbeitrag und verwendet MutationObserver .

// Select the node that will be observed for mutations
var targetNode = document.getElementById('some-id');

// Options for the observer (which mutations to observe)
var config = { attributes: true, childList: true };

// Callback function to execute when mutations are observed
var callback = function(mutationsList) {
    for(var mutation of mutationsList) {
        if (mutation.type == 'childList') {
            console.log('A child node has been added or removed.');
        }
        else if (mutation.type == 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
        }
    }
};

// Create an observer instance linked to the callback function
var observer = new MutationObserver(callback);

// Start observing the target node for configured mutations
observer.observe(targetNode, config);

// Later, you can stop observing
observer.disconnect();

Browser-Unterstützung: Chrome 18+, Firefox 14+, IE 11+, Safari 6+

16voto

Adam Pietrasiak Punkte 11497

Ich habe kürzlich ein Plugin geschrieben, das genau das tut - jquery.initialize

Sie verwenden es auf die gleiche Weise wie .each Funktion

$(".some-element").initialize( function(){
    $(this).css("color", "blue"); 
});

Der Unterschied zu .each ist - er nimmt Ihren Selektor, in diesem Fall .some-element und warten auf neue Elemente mit diesem Selektor in der Zukunft, wenn ein solches Element hinzugefügt wird, wird es auch initialisiert werden.

In unserem Fall ändert die Initialisierungsfunktion einfach die Farbe des Elements in Blau. Also, wenn wir ein neues Element hinzufügen (egal, ob mit Ajax oder sogar F12 Inspektor oder etwas) wie:

$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!

Das Plugin wird es sofort starten. Außerdem sorgt das Plugin dafür, dass ein Element nur einmal initialisiert wird. Wenn Sie also ein Element hinzufügen, dann .detach() Wenn Sie es aus dem Körper entfernen und dann wieder hinzufügen, wird es nicht erneut initialisiert.

$("<div/>").addClass('some-element').appendTo("body").detach()
    .appendTo(".some-container");
//initialized only once

Das Plugin basiert auf MutationObserver - es funktioniert mit IE9 und 10 mit den Abhängigkeiten, wie sie in der Liesmich-Seite .

14voto

Mahdi Bashirpour Punkte 13425
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
  //...
});

Vollständige Erklärungen: https://stackoverflow.com/a/11546242/6569224

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X