jquery che rileva div di una determinata classe è stato aggiunto a DOM


90

Sto usando .on()per associare eventi di div che vengono creati dopo il caricamento della pagina. Funziona bene per click, mouseenter ... ma ho bisogno di sapere quando è stato aggiunto un nuovo div della classe MyClass. Sto cercando questo:

$('#MyContainer').on({

  wascreated: function () { DoSomething($(this)); }

}, '.MyClass');

Come faccio a fare questo? Sono riuscito a scrivere la mia intera app senza un plug-in e voglio mantenerla così.

Grazie.


È il tuo codice che genera gli elementi? In tal caso, puoi controllare nel momento in cui lo crei e agire di conseguenza.
Surreal Dreams

Sì, è vero ma con .on (), posso associare eventi a document.ready e quindi non devo preoccuparmi dell'associazione quando genero l'HTML. Sto cercando lo stesso comportamento con DoSomething.
Frenchie

puoi usare $ ("div / your-selector"). hasClass ("MyClass"); per verificare se un div ha la classe particolare o meno. Immagino che potresti mettere questa API a tuo uso.
KBN

.on () è solo per eventi. Potresti impostare un evento personalizzato, ma dovresti comunque attivare quell'evento quando crei nuovi elementi.
Surreal Dreams

Risposte:


102

In precedenza si poteva collegarsi al domManipmetodo di jQuery per catturare tutte le manipolazioni di jQuery dom e vedere quali elementi erano inseriti ecc. Ma il team di jQuery lo ha spento in jQuery 3.0+ in quanto generalmente non è una buona soluzione agganciarsi ai metodi jQuery in questo modo, e loro ' Abbiamo fatto in modo che il domManipmetodo interno non sia più disponibile al di fuori del codice jQuery principale.

Anche gli eventi di mutazione sono stati deprecati, poiché prima si poteva fare qualcosa di simile

$(document).on('DOMNodeInserted', function(e) {
    if ( $(e.target).hasClass('MyClass') ) {
       //element with .MyClass was inserted.
    }
});

questo dovrebbe essere evitato, e oggi dovrebbero essere usati gli osservatori di mutazione, che funzionerebbero in questo modo

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        console.log(mutation)
        if (mutation.addedNodes && mutation.addedNodes.length > 0) {
            // element added to DOM
            var hasClass = [].some.call(mutation.addedNodes, function(el) {
                return el.classList.contains('MyClass')
            });
            if (hasClass) {
                // element has class `MyClass`
                console.log('element ".MyClass" added');
            }
        }
    });
});

var config = {
    attributes: true,
    childList: true,
    characterData: true
};

observer.observe(document.body, config);

Firefox e webkit dovrebbero essere praticamente OK, ma il supporto per questo è piuttosto scarso in IE. Dovrebbe funzionare in IE9, ma probabilmente non così tanto in IE8. So di aver visto soluzioni alternative e non l'ho realmente testato in IE, quindi provalo prima, se non funziona, controlla se c'è una soluzione alternativa. C'è un plugin che sembra aggiungere il supporto IE qui , ma non l'ho provato, l'ho appena trovato in una ricerca su Google.
adeneo

2
Inizialmente ho usato la tua risposta, ma il fatto che non funzionasse su tutti i browser era un problema. Quindi, ho trovato rapidamente una soluzione per evitare questo problema in primo luogo. E poi, alcune settimane fa, questo problema di rilevamento dell'inserimento del nodo è tornato di nuovo nel mio codice. Questa volta, il codice che ho pubblicato nella mia risposta funziona ESATTAMENTE come dovrebbe, cross-browser e niente bug. Ecco perché ho spostato la mia risposta come accettata; è così che funziona SO, l'OP decide quale risposta accetta e la folla vota sulle risposte. Nel tempo, le persone voteranno e decideranno quale risposta si adatta meglio a loro.
Frenchie

2
Inoltre, volevo dire che mi hai aiutato più volte con risposte di alta qualità che mi hanno salvato la giornata. La modifica della risposta accettata alla mia si basa esclusivamente sul codice e su come si adatta al problema che ho dovuto risolvere.
Frenchie

ha ottenuto "Uncaught TypeError: Cannot read property 'contains' of undefined" con questo codice
Gordie

53

Ecco il mio plugin che fa esattamente questo: jquery.initialize

L'utilizzo è lo stesso come si usa la .eachfunzione, ma con la .initializefunzione sull'elemento, a differenza di.each è che inizializzerà anche gli elementi aggiunti in futuro senza alcun codice aggiuntivo, non importa se lo aggiungi con AJAX o qualsiasi altra cosa.

Initialize ha esattamente la stessa sintassi della funzione .each

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

Ma ora se sulla pagina apparirà un nuovo elemento che corrisponde a .some-element selector, verrà inizializzato istantaneamente. Il modo in cui viene aggiunto il nuovo elemento non è importante, non è necessario preoccuparsi di eventuali richiamate ecc.

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

Il plugin è basato su MutationObserver


Grazie mille.
Brandon Harris

come fermare l'inizializzazione? Voglio smettere di guardare una volta che le cose che stavo cercando sono state aggiunte ...
Evgeny Vostok

$ (". some-element"). initialize [..] è deprecato. Usa $ .initialize (". AddDialog", function () {$ ('. NoData'). Hide ();}); anziché.
Heimi

È possibile eseguire la .initializefunzione callback solo per gli elementi aggiunti dinamicamente (ad esempio tramite Ajax) e saltare il callback per gli elementi già presenti quando la pagina viene caricata inizialmente? Sto cercando di aggiungere un parametro a optionsper ottenere ciò, ma finora non ho funzionato.
Nevermore

Puoi aggiungere un problema su GitHub al riguardo. Per ora, tecnicamente potresti fare$('.myClass').addClass('ignore-initialize'); $.initialize('.myClass:not(.ignore-initialize)', callback);
pie6k

19

Dopo aver esaminato questo e molti altri post, ho cercato di distillare ciò che pensavo fosse il migliore di ciascuno in qualcosa di semplice che mi permettesse di rilevare quando una classe di elementi viene inserita e quindi agire su quegli elementi .

function onElementInserted(containerSelector, elementSelector, callback) {

    var onMutationsObserved = function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.addedNodes.length) {
                var elements = $(mutation.addedNodes).find(elementSelector);
                for (var i = 0, len = elements.length; i < len; i++) {
                    callback(elements[i]);
                }
            }
        });
    };

    var target = $(containerSelector)[0];
    var config = { childList: true, subtree: true };
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    var observer = new MutationObserver(onMutationsObserved);    
    observer.observe(target, config);

}

onElementInserted('body', '.myTargetElement', function(element) {
    console.log(element);
});

È importante sottolineare che per me questo consente a) l'elemento di destinazione di esistere a qualsiasi profondità all'interno di "addedNodes" eb) la capacità di gestire l'elemento solo quando inizialmente inserito (non è necessario cercare nell'intero documento l'impostazione o ignorare "già elaborato" bandiere).


1
La tua condizione dovrebbe essere if(mutation.addedNodes.length)perché addedNodese gli removedNodesarray sono sempre presenti nel mutationtipo childList(controllato in IE11, Chrome53, FF49). Ancora +1, perché la tua è l'unica risposta in cui addedNodesviene considerato ( callbackviene chiamato solo quando viene aggiunto un nodo); non solo su questo thread ma su molti altri thread. Tutti gli altri stanno semplicemente invocando il callbackper tutte le mutazioni, anche se il nodo viene cancellato.
Vivek Athalye,

17

3 anni di esperienza dopo, è così che ascolto "elemento di una certa classe aggiunto al DOM" : aggiungi semplicemente un hook alla html()funzione jQuery , in questo modo:

function Start() {

   var OldHtml = window.jQuery.fn.html;

   window.jQuery.fn.html = function () {

     var EnhancedHtml = OldHtml.apply(this, arguments);

     if (arguments.length && EnhancedHtml.find('.MyClass').length) {

         var TheElementAdded = EnhancedHtml.find('.MyClass'); //there it is
     }

     return EnhancedHtml;
   }
}

$(Start);

Funziona se stai usando jQuery, cosa che faccio io. E non si basa sull'evento specifico del browser DOMNodeInserted, che non è compatibile con tutti i browser. Ho anche aggiunto la stessa implementazione per.prepend()

Nel complesso, questo funziona come un fascino per me e, si spera, anche per te.


6
Hmm - Vorrei chiarire che questo funziona se stai usando esclusivamente l' htmlhelper di jQuery per modificare il DOM, non solo "usando jQuery" in generale. Gli elementi aggiunti usando, diciamo, jQuery.appendnon attiveranno questo evento.
iameli

1
Sì, ed è per questo che ho chiarito che ho aggiunto la stessa implementazione con .preprend () e se usi .append () allora fai la stessa cosa con .append ()
frenchie

C'è un approccio molto più veloce, usando le animazioni CSS3 . La libreria open-source presta anche a codice molto più semplice:insertionQ('#intercom-container').every(function(/element){ element.style.display = 'none'; });
Dan Dascalescu

3
Perché il voto negativo ?? Funziona davvero: nessun plugin e nessuna funzione setTimeout hacky.
Frenchie

2
Upvoted - questa è un'opzione valida - perché nel mondo (!!!) questo è stato downvoted ?????? Dan, CSS3 Animations non sono XBC: caniuse.com/#search=keyframes ; davidwalsh.name/detect-node-insertion .
Cody

6

potresti usare eventi di mutazione

http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-mutationevents

MODIFICARE

da MDN: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events

Deprecato Questa funzionalità è stata rimossa dal Web. Sebbene alcuni browser possano ancora supportarlo, è in procinto di essere abbandonato. Non utilizzarlo in progetti vecchi o nuovi. Le pagine o le app Web che lo utilizzano potrebbero interrompersi in qualsiasi momento.

Gli osservatori di mutazione sono la sostituzione proposta per gli eventi di mutazione in DOM4. Devono essere inclusi in Firefox 14 e Chrome 18.

https://developer.mozilla.org/en/docs/Web/API/MutationObserver

MutationObserverfornisce agli sviluppatori un modo per reagire ai cambiamenti in un DOM. È progettato come sostituto degli eventi di mutazione definiti nella specifica degli eventi DOM3.

Utilizzo di esempio

Il seguente esempio è stato preso da http://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/ .

// select the target node
var target = document.querySelector('#some-id');

// create an observer instance
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log(mutation.type);
  });    
});

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

2
Gli eventi di mutazione sono praticamente deprecati secondo mdn , a favore di MutationObservers
Olga

Ora che IE10 è EOL, dovrebbe essere quasi sicuro dire che MutationObserver è ampiamente supportato.
rymo

0

Due aggiunte alla risposta di Adeneo:

(1) possiamo cambiare la linea

return el.classList.contains('MyClass');

Per

if( el.classList ) {
    return el.classList.contains('MyClass');
} else {
    return false;
}

Quindi non vedremo l' "Uncaught TypeError: Cannot read property 'contains' of undefined"errore.

(2) Aggiungere subtree: truea configtrovare i nodi aggiunti ricorsivamente in tutti gli elementi aggiunti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.