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.


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

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

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


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


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ż.


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

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.