Domanda

Voglio eseguire una funzione quando alcuni div o input vengono aggiunti all'HTML. È possibile?

Ad esempio, viene aggiunto un ingresso di testo, quindi è necessario chiamare la funzione.

È stato utile?

Soluzione

Aggiornamento 2015, nuovo MutationObserver è supportato dai moderni browser :

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

Se è necessario supportare quelli più vecchi, potresti provare a tornare ad altri approcci come quelli menzionati in questa risposta 5 (!) Anno antico di seguito. Ci sono draghi. Goditi :)


.

Qualcun altro sta cambiando il documento? Poiché se hai il pieno controllo sulle modifiche è solo necessario creare la propria API domChanged - con una funzione o un evento personalizzato e attivare / chiamarlo ovunque modifichi le cose.

Il Dom Level-2 ha Tipi di eventi della mutazione , ma la versione precedente di IE non lo supporta. Si noti che gli eventi di mutazione sono deprecated negli eventi DOM3 Spec e avere un Penalità di performance . Puoi provare ad emulare l'evento di mutazione con onpropertychange in IE (e ricaduta all'approccio di forza bruta se non di loro è disponibile).

per A FULL Domchange Un intervallo potrebbe essere un'over-kill. Immagina di essere necessario memorizzare lo stato attuale dell'intero documento ed esaminare l'elemento che ogni proprietà è lo stesso.

Forse se sei interessato solo agli elementi e al loro ordine (come hai detto nella tua domanda), un getElementsByTagName("*") può funzionare. Ciò accenderà automaticamente se si aggiunge un elemento, rimuovere un elemento, sostituire elementi o modificare la struttura del documento.

Ho scritto una prova del concetto:

(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);
.

Uso:

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

funziona su IE 5.5+, FF 2+, Chrome, Safari 3+ e Opera 9.6 +

Altri suggerimenti

Questo è il prossimo approccio finora, con il codice più piccolo:

IE9 +, FF, WebKit:

.

Usando mutationobserver e ricadendo al deprecato eventi di mutazione se necessario:
(esempio seguente se solo per le modifiche a DOM riguardanti i nodi aggiunti o rimossi)

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

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

    if( MutationObserver ){
      // define a new observer
      var obs = new MutationObserver(function(mutations, observer){
          callback(mutations);
      })
      // have the observer observe foo for changes in children
      obs.observe( obj, { childList:true, subtree:true });
    }
    
    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>
.

Ho recentemente scritto un plugin che fa esattamente questo - jquery.initialize Lo usi allo stesso modo della funzione .each

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

La differenza da .each è - prende il selettore, in questo caso .some-element e attendere nuovi elementi con questo selettore in futuro, se tale elemento verrà aggiunto, sarà anche inizializzato.

Nel nostro caso inizializzare la funzione basta cambiare colore dell'elemento in blu. Quindi se aggiungeremo un nuovo elemento (non importa se con Ajax o anche ispettore F12 o altro) come:

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

Plugin inizierà immediatamente. Anche il plugin assicura che un elemento sia inizializzato solo una volta. Quindi, se aggiungi elemento, allora .detach() dal corpo e quindi aggiungilo di nuovo, non verrà inizializzato di nuovo.

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

Plugin è basato su MutationObserver - Lavorerà su IE9 e 10 con dipendenze come dettagliato sul Pagina README .

o puoi semplicemente Crea il tuo evento , che funziona ovunque

 $("body").on("domChanged", function () {
                //dom is changed 
            });


 $(".button").click(function () {

          //do some change
          $("button").append("<span>i am the new change</span>");

          //fire event
          $("body").trigger("domChanged");

        });
.

Esempio completo http://jsfiddle.net/hbmaam/mq7nx/

L'esempio seguente è stato adattato da Mozilla Hacks ' post blog e sta usando 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();
.

Supporto del browser: Chrome 18+, Firefox 14+, IE 11+, Safari 6 +

Usa il mutationobserver interfaccia come mostrato in Gabriele Romanato blog

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

// The node to be monitored
var target = $( "#content" )[0];

// Create an observer instance
var observer = new MutationObserver(function( mutations ) {
  mutations.forEach(function( mutation ) {
    var newNodes = mutation.addedNodes; // DOM NodeList
    if( newNodes !== null ) { // If there are new nodes added
        var $nodes = $( newNodes ); // jQuery set
        $nodes.each(function() {
            var $node = $( this );
            if( $node.hasClass( "message" ) ) {
                // do something
            }
        });
    }
  });    
});

// Configuration of the observer:
var config = { 
    attributes: true, 
    childList: true, 
    characterData: true 
};

// Pass in the target node, as well as the observer options
observer.observe(target, config);

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

Che ne dici di estendere una jQuery per questo?

   (function () {
        var ev = new $.Event('remove'),
            orig = $.fn.remove;
        var evap = new $.Event('append'),
           origap = $.fn.append;
        $.fn.remove = function () {
            $(this).trigger(ev);
            return orig.apply(this, arguments);
        }
        $.fn.append = function () {
            $(this).trigger(evap);
            return origap.apply(this, arguments);
        }
    })();
    $(document).on('append', function (e) { /*write your logic here*/ });
    $(document).on('remove', function (e) { /*write your logic here*/ ) });
.

JQuery 1.9+ ha costruito il supporto per questo (ho sentito non testato).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top