jquery wykrywający element div określonej klasy został dodany do DOM

90

Używam .on()do wiązania zdarzeń div, które są tworzone po załadowaniu strony. Działa dobrze w przypadku kliknięcia, klawisza myszy ... ale muszę wiedzieć, kiedy został dodany nowy element div klasy MyClass. Szukam tego:

$('#MyContainer').on({

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

}, '.MyClass');

Jak mam to zrobic? Udało mi się napisać całą moją aplikację bez wtyczki i chcę, żeby tak zostało.

Dzięki.

Francuz
źródło
Czy to Twój własny kod generuje elementy? Jeśli tak, możesz to sprawdzić w momencie tworzenia i odpowiednio postępować.
Surreal Dreams
Tak, to prawda, ale dzięki .on () mogę wiązać zdarzenia w document.ready i nie muszę się wtedy martwić o powiązanie podczas generowania kodu HTML. Szukam tego samego zachowania z DoSomething.
Francuz
możesz użyć $ ("div / your-selector"). hasClass ("MyClass"); aby sprawdzić, czy element div ma określoną klasę, czy nie. Myślę, że możesz użyć tego interfejsu API do użytku.
KBN
.on () jest tylko dla wydarzeń. Możesz skonfigurować zdarzenie niestandardowe, ale nadal będziesz musiał wywołać to zdarzenie podczas tworzenia nowych elementów.
Surreal Dreams
3
Duplikat wydarzenia po dodaniu elementu do strony
Dan Dascalescu

Odpowiedzi:

102

Wcześniej można było podłączyć się do domManipmetody jQuery, aby złapać wszystkie manipulacje jQuery dom i zobaczyć, które elementy zostały wstawione itp., Ale zespół jQuery zamknął to w jQuery 3.0+, ponieważ generalnie nie jest dobrym rozwiązaniem podłączanie się do metod jQuery w ten sposób, a oni ' sprawiliśmy, że domManipmetoda wewnętrzna nie jest już dostępna poza podstawowym kodem jQuery.

Zdarzenia mutacji również zostały wycofane, ponieważ wcześniej można było zrobić coś takiego

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

Należy tego unikać, a obecnie zamiast tego należy używać Obserwatorów Mutacji, które działałyby w ten sposób

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
źródło
Firefox i webkit powinny być w porządku, ale obsługa tego jest dość słaba w IE. Powinien działać w IE9, ale prawdopodobnie nie tak bardzo w IE8. Wiem, że widziałem obejścia i tak naprawdę nie testowałem tego w IE, więc najpierw przetestuj, jeśli nie działa, sprawdź, czy istnieje obejście. Jest wtyczka, która wygląda tak, jakby dodawała tutaj obsługę IE , ale jej nie próbowałem, po prostu znalazłem ją w wyszukiwarce Google.
adeneo
2
Początkowo użyłem Twojej odpowiedzi, ale fakt, że nie działała ona w różnych przeglądarkach, był problemem. Potem szybko znalazłem obejście, aby uniknąć tego problemu. A potem, kilka tygodni temu, problem wykrywania wstawiania węzłów powrócił w moim kodzie. Tym razem kod, który zamieściłem w mojej odpowiedzi, działa DOKŁADNIE tak, jak powinien, między przeglądarkami i nic nie zawiera błędów. Dlatego właśnie ustawiłem moją odpowiedź jako zaakceptowaną; tak działa SO, PO decyduje, którą odpowiedź zaakceptuje, a tłum głosuje na odpowiedzi. Z biegiem czasu ludzie będą głosować i decydować, która odpowiedź najbardziej im odpowiada.
Frenchie
2
Chciałem też powiedzieć, że kilkakrotnie pomogłeś mi udzielając wysokiej jakości odpowiedzi, które uratowały mi dzień. Zmiana zaakceptowanej odpowiedzi na moją zależy wyłącznie od kodu i tego, jak pasuje do problemu, który musiałem rozwiązać.
frenchie
otrzymano „Uncaught TypeError: Cannot read property 'contains' of undefined” z tym kodem
gordie
53

Oto moja wtyczka, która dokładnie to robi - jquery.initialize

Użycie jest takie samo, jak w przypadku .eachfunkcji, ale w przypadku .initializefunkcji na elemencie, różnica .eachpolega na tym, że inicjalizuje elementy dodane w przyszłości bez dodatkowego kodu - bez względu na to, czy dodasz go za pomocą AJAX, czy cokolwiek innego.

Initialize ma dokładnie taką samą składnię jak w przypadku funkcji .each

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

Ale teraz, jeśli na stronie pojawi się nowy element pasujący do selektora niektórych elementów, zostanie on zainicjowany w trybie natychmiastowym. Sposób dodawania nowej pozycji nie jest ważny, nie musisz przejmować się żadnymi callbackami itp.

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

Plugin jest oparty na MutationObserver

pie6k
źródło
Dziękuję bardzo.
Brandon Harris
jak zatrzymać inicjalizację? Chcę przestać oglądać, gdy coś, czego szukałem, zostanie dodane ...
Evgeny Vostok,
$ (". jakiś element"). initialize [..] jest przestarzałe. Użyj $ .initialize (". AddDialog", function () {$ ('. NoData'). Hide ();}); zamiast.
Heimi
Czy możliwe jest wykonanie .initializefunkcji callback tylko dla elementów, które są dynamicznie dodawane (np. Przez Ajax) i pominięcie wywołania zwrotnego dla elementów już obecnych podczas ładowania strony? Próbuję dodać parametr do options, aby to osiągnąć, ale jak dotąd nie mam tego do pracy.
Nevermore
Możesz o tym dodać problem na githubie. Na razie technicznie $('.myClass').addClass('ignore-initialize'); $.initialize('.myClass:not(.ignore-initialize)', callback);
dało się
19

Po przejrzeniu tego i kilku innych postów próbowałem wydestylować to, co uważałem za najlepsze z każdego z nich, w coś prostego, co pozwoliło mi wykryć, kiedy wstawiana jest klasa elementów, a następnie działać na tych elementach .

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

Co ważne dla mnie, pozwala to na a) istnienie elementu docelowego na dowolnej głębokości w ramach „addedNodes” oraz b) możliwość obsługi elementu tylko przy jego pierwotnym wstawieniu (nie ma potrzeby przeszukiwania całego ustawienia dokumentu ani ignorowania „już przetworzonego”) flagi).

John Clegg
źródło
1
Twój stan powinien być if(mutation.addedNodes.length), bo addedNodesi removedNodestablice są zawsze w mutationrodzaju childList(sprawdzane w IE11, Chrome53, FF49). Wciąż +1, ponieważ twoja jest jedyną odpowiedzią, w której addedNodesjest brana pod uwagę ( callbackjest wywoływana tylko wtedy, gdy dodawany jest węzeł); nie tylko w tym wątku, ale w wielu innych. Wszyscy inni po prostu wywołują callbackfor all mutations, nawet jeśli węzeł jest usuwany.
Vivek Athalye
17

3 lata później doświadczenia, tak słucham „elementu pewnej klasy dodanego do DOM” : po prostu dodajesz hook do html()funkcji jQuery , na przykład:

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

To działa, jeśli używasz jQuery, co robię. I nie polega na zdarzeniu specyficznym dla przeglądarkiDOMNodeInserted , które nie jest kompatybilne z różnymi przeglądarkami. Dodałem również tę samą implementację dla.prepend()

Ogólnie rzecz biorąc, działa to jak urok dla mnie i mam nadzieję, że dla ciebie też.

Francuz
źródło
6
Hmm - wyjaśniłbym, że to działa, jeśli używasz wyłącznie htmlpomocnika jQuery do modyfikowania DOM, a nie tylko „używania jQuery” w ogóle. Elementy dodane za pomocą, powiedzmy, jQuery.appendnie wywołają tego zdarzenia.
iameli
1
Tak, i dlatego wyjaśniłem, że dodałem tę samą implementację z .preprend () i jeśli używasz .append (), zrób to samo z .append ()
frenchie
Istnieje znacznie szybsze podejście, używając animacji CSS3 . Biblioteka open source udostępnia również znacznie prostszy kod:insertionQ('#intercom-container').every(function(/element){ element.style.display = 'none'; });
Dan Dascalescu
3
Dlaczego głosy przeciw ?? To faktycznie działa: bez wtyczki i bez hakerskiej funkcji setTimeout.
Francuz
2
Za głosem - to poprawna opcja - dlaczego na świecie (!!!) została ona odrzucona ?????? Dan, animacje CSS3 nie są XBC: caniuse.com/#search=keyframes ; davidwalsh.name/detect-node-insertion .
Cody
6

możesz użyć zdarzeń mutacji

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

EDYTOWAĆ

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

Przestarzałe Ta funkcja została usunięta z internetu. Chociaż niektóre przeglądarki nadal mogą go obsługiwać, jest w trakcie usuwania. Nie używaj go w starych lub nowych projektach. Strony lub aplikacje internetowe, które go używają, mogą się zepsuć w dowolnym momencie.

Obserwatorzy mutacji są proponowanym zamiennikiem zdarzeń mutacji w DOM4. Mają być zawarte w przeglądarkach Firefox 14 i Chrome 18.

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

MutationObserverzapewnia programistom sposób reagowania na zmiany w DOM. Został zaprojektowany jako zamiennik zdarzeń mutacji zdefiniowanych w specyfikacji DOM3 Events.

Przykładowe użycie

Poniższy przykład pochodzi z 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
źródło
2
Zdarzenia mutacji są prawie przestarzałe według mdn , na korzyść MutationObservers
Olga
Teraz, gdy IE10 jest EOL, powinno być prawie bezpieczne, aby powiedzieć, że MutationObserver jest szeroko obsługiwany.
rymo
0

Dwa dodatki do odpowiedzi adeneo:

(1) możemy zmienić linię

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

Do

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

Więc nie zobaczymy "Uncaught TypeError: Cannot read property 'contains' of undefined" błędu.

(2) dodatek subtree: truew configcelu znalezienia dodano węzłów rekurencyjnie wszystkich dodawanych pierwiastków.

agrul
źródło