JOM, das Div einer bestimmten Klasse erkennt, wurde zu DOM hinzugefügt

87

Ich verwende .on(), um Ereignisse von Divs zu binden, die nach dem Laden der Seite erstellt werden. Es funktioniert gut für Click, Mouseenter ... aber ich muss wissen, wann ein neues Div der Klasse MyClass hinzugefügt wurde. Ich suche das:

$('#MyContainer').on({

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

}, '.MyClass');

Wie mache ich das? Ich habe es geschafft, meine gesamte App ohne Plugin zu schreiben und möchte es auch so halten.

Vielen Dank.

frenchie
quelle
Ist es Ihr eigener Code, der die Elemente generiert? In diesem Fall können Sie zum Zeitpunkt der Erstellung überprüfen und entsprechend handeln.
Surreale Träume
Ja, das stimmt, aber mit .on () kann ich Ereignisse in document.ready binden, und dann muss ich mich beim Generieren des HTML-Codes nicht um die Bindung kümmern. Ich suche das gleiche Verhalten mit DoSomething.
Frenchie
Sie können $ ("div / your-selector") verwenden. hasClass ("MyClass"); um zu überprüfen, ob ein div die bestimmte Klasse hat oder nicht. Ich denke, Sie könnten diese API zu Ihrem Gebrauch verwenden.
KBN
.on () ist nur für Ereignisse. Sie können ein benutzerdefiniertes Ereignis einrichten, müssen dieses Ereignis jedoch weiterhin auslösen, wenn Sie neue Elemente erstellen.
Surreale Träume

Antworten:

99

Früher konnte man sich in die jQuery- domManipMethode einbinden, um alle jQuery-Dom-Manipulationen abzufangen und zu sehen, welche Elemente eingefügt wurden usw., aber das jQuery-Team hat dies in jQuery 3.0+ beendet, da es im Allgemeinen keine gute Lösung ist, sich auf diese Weise in jQuery-Methoden einzubinden. Wir haben es so gemacht, dass die interne domManipMethode außerhalb des jQuery-Kerncodes nicht mehr verfügbar ist.

Mutationsereignisse sind ebenfalls veraltet, so wie man es vorher tun konnte

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

Dies sollte vermieden werden, und heute sollten stattdessen Mutationsbeobachter verwendet werden, was so funktionieren würde

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);
Adeneo
quelle
Firefox und Webkit sollten in Ordnung sein, aber die Unterstützung dafür ist im IE etwas schlecht. Es sollte in IE9 funktionieren, aber wahrscheinlich nicht so sehr in IE8. Ich weiß, dass ich Problemumgehungen gesehen habe, und ich habe dies im IE nicht wirklich getestet. Testen Sie es also zuerst. Wenn es nicht funktioniert, prüfen Sie, ob es eine Problemumgehung gibt. Es gibt ein Plugin, das anscheinend IE-Unterstützung hier hinzufügt , es aber nicht ausprobiert hat , sondern nur in einer Google-Suche gefunden hat.
Adeneo
2
Ich habe Ihre Antwort zunächst verwendet, aber die Tatsache, dass sie nicht browserübergreifend funktioniert, war ein Problem. Dann habe ich schnell eine Lösung gefunden, um dieses Problem überhaupt zu vermeiden. Und dann, vor ein paar Wochen, tauchte dieses Problem der Erkennung des Einfügens von Knoten in meinem Code wieder auf. Diesmal funktioniert der Code, den ich in meiner Antwort gepostet habe, genau so, wie er sollte, browserübergreifend und nichts fehlerhaftes. Deshalb habe ich meine Antwort als akzeptiert verschoben. So funktioniert SO, das OP entscheidet, welche Antwort er akzeptiert und die Menge stimmt über die Antworten ab. Im Laufe der Zeit werden die Menschen abstimmen und entscheiden, welche Antwort am besten zu ihnen passt.
Frenchie
2
Außerdem wollte ich sagen, dass Sie mir mehrmals mit hochwertigen Antworten geholfen haben, die mir den Tag gerettet haben. Das Ändern der akzeptierten Antwort auf meine basiert ausschließlich auf dem Code und darauf, wie er zu dem Problem passt, das ich lösen musste.
Frenchie
bekam "Uncaught TypeError: Eigenschaft 'enthält' von undefined kann nicht gelesen werden" mit diesem Code
gordie
53

Hier ist mein Plugin, das genau das tut - jquery.initialize

Die Verwendung ist dieselbe wie bei der Verwendung der .eachFunktion, aber mit der .initializeFunktion für das Element .eachwerden auch Elemente, die in Zukunft hinzugefügt werden, ohne zusätzlichen Code initialisiert - unabhängig davon, ob Sie sie mit AJAX oder etwas anderem hinzufügen.

Initialize haben genau die gleiche Syntax wie die Funktion .each

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

Wenn nun auf der Seite ein neues Element angezeigt wird, das mit der Auswahl für einige Elemente übereinstimmt, wird es sofort initialisiert. Die Art und Weise, wie neue Elemente hinzugefügt werden, ist nicht wichtig. Sie müssen sich nicht um Rückrufe usw. kümmern.

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

Plugin basiert auf MutationObserver

pie6k
quelle
Ich danke dir sehr.
Brandon Harris
Wie stoppe ich die Initialisierung? Ich möchte aufhören zu schauen, sobald das Zeug, nach dem ich gesucht habe, hinzugefügt wurde ...
Evgeny Vostok
$ (". some-element"). initialize [..] ist veraltet. Verwenden Sie $ .initialize (". AddDialog", function () {$ ('. NoData'). Hide ();}); stattdessen.
Heimi
Ist es möglich, die .initializeFunktionen callback nur für Elemente auszuführen , die dynamisch hinzugefügt werden (z. B. über Ajax), und den Rückruf für Elemente zu überspringen, die bereits beim ersten Laden der Seite vorhanden sind? Ich versuche, einen Parameter hinzuzufügen, optionsum dies zu erreichen, aber bisher habe ich dies nicht zum Laufen gebracht.
Nie mehr
Sie können ein Problem auf Github hinzufügen. Im $('.myClass').addClass('ignore-initialize'); $.initialize('.myClass:not(.ignore-initialize)', callback);
Moment
18

Nachdem ich diesen und mehrere andere Beiträge durchgesehen hatte, versuchte ich, das, was ich für das Beste von jedem hielt, in etwas Einfaches zu destillieren, mit dem ich erkennen konnte, wann eine Klasse von Elementen eingefügt wurde, und dann auf diese Elemente einwirken konnte .

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);
});

Für mich ist es wichtig, dass a) das Zielelement in jeder Tiefe innerhalb von "addedNodes" vorhanden ist und b) das Element nur beim erstmaligen Einfügen verarbeitet werden kann (keine Notwendigkeit, die gesamte Dokumenteinstellung zu durchsuchen oder "bereits verarbeitet" zu ignorieren. Flaggen).

jtc
quelle
1
Ihr Zustand sollte sein, if(mutation.addedNodes.length)weil addedNodesund removedNodesArrays immer mutationvom Typ vorhanden sind childList(geprüft in IE11, Chrome53, FF49). Immer noch +1, da Ihre die einzige Antwort addedNodesist, die berücksichtigt wird ( callbackwird nur aufgerufen, wenn ein Knoten hinzugefügt wird); nicht nur auf diesem Thread, sondern auch auf vielen anderen Threads. Alle anderen rufen nur die callbackfür alle Mutationen auf, auch wenn der Knoten gelöscht wird.
Vivek Athalye
16

3 Jahre Erfahrung später höre ich so "Element einer bestimmten Klasse, die dem DOM hinzugefügt wurde" : Sie fügen einfach einen Hook in die jQuery- html()Funktion ein, wie folgt:

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

Dies funktioniert, wenn Sie jQuery verwenden, was ich auch tue. Und es hängt nicht von dem browserspezifischen Ereignis ab DOMNodeInserted, das nicht browserübergreifend kompatibel ist. Ich habe auch die gleiche Implementierung für hinzugefügt.prepend()

Insgesamt wirkt dies wie ein Zauber für mich und hoffentlich auch für Sie.

frenchie
quelle
6
Hmm - Ich möchte klarstellen, dass dies funktioniert, wenn Sie ausschließlich den htmlHelfer von jQuery verwenden , um das DOM zu ändern, und nicht nur "jQuery verwenden" im Allgemeinen. Elemente, die beispielsweise mit hinzugefügt wurden, jQuery.appendlösen dieses Ereignis nicht aus.
Iameli
1
Ja, und deshalb habe ich klargestellt, dass ich dieselbe Implementierung mit .preprend () hinzugefügt habe. Wenn Sie .append () verwenden, machen Sie dasselbe mit .append ()
frenchie
Es gibt einen viel schnelleren Ansatz mit CSS3-Animationen . Die Open-Source-Bibliothek insertionQ('#intercom-container').every(function(/element){ element.style.display = 'none'; });
bietet
3
Warum das Downvote? Das funktioniert tatsächlich: kein Plugin und keine hackige setTimeout-Funktion.
Frenchie
2
Upvoted - dies ist eine gültige Option - warum in aller Welt (!!!) wurde dies downvoted ?????? Dan, CSS3-Animationen sind keine XBC: caniuse.com/#search=keyframes ; davidwalsh.name/detect-node-insertion .
Cody
6

Sie könnten Mutationsereignisse verwenden

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

BEARBEITEN

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

Veraltet Diese Funktion wurde aus dem Web entfernt. Obwohl einige Browser dies möglicherweise noch unterstützen, wird es gerade gelöscht. Verwenden Sie es nicht in alten oder neuen Projekten. Seiten oder Web-Apps, die es verwenden, können jederzeit beschädigt werden.

Mutationsbeobachter sind der vorgeschlagene Ersatz für Mutationsereignisse in DOM4. Sie sollen in Firefox 14 und Chrome 18 enthalten sein.

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

MutationObserverbietet Entwicklern die Möglichkeit, auf Änderungen in einem DOM zu reagieren. Es ist als Ersatz für Mutationsereignisse konzipiert, die in der DOM3-Ereignisspezifikation definiert sind.

Anwendungsbeispiel

Das folgende Beispiel stammt von 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();
RGB
quelle
2
Mutationsereignisse sind laut mdn zugunsten von MutationObservers
Olga
Jetzt, da IE10 EOL ist, sollte es fast sicher sein zu sagen, dass MutationObserver weitgehend unterstützt wird.
Rymo
0

Zwei Ergänzungen zu Adeneos Antwort:

(1) Wir können die Zeile ändern

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

Zu

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

Wir werden den "Uncaught TypeError: Cannot read property 'contains' of undefined"Fehler also nicht sehen .

(2) Add- subtree: trueIn config, um hinzugefügte Knoten rekursiv in allen hinzugefügten Elementen zu finden.

agrul
quelle