jquery detecting div dari kelas tertentu telah ditambahkan ke DOM

90

Saya menggunakan .on()untuk mengikat acara div yang dibuat setelah halaman dimuat. Ini berfungsi dengan baik untuk klik, mouseenter ... tetapi saya perlu tahu kapan div baru kelas MyClass telah ditambahkan. Saya mencari ini:

$('#MyContainer').on({

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

}, '.MyClass');

Bagaimana saya melakukan ini? Saya telah berhasil menulis seluruh aplikasi saya tanpa plugin dan saya ingin tetap seperti itu.

Terima kasih.

frenchie
sumber
Apakah kode Anda sendiri yang menghasilkan elemen? Jika demikian, Anda dapat memeriksa pada saat Anda membuatnya dan bertindak sesuai dengan itu.
Surreal Dreams
Ya, itu benar tetapi Dengan .on (), saya bisa mengikat acara di document.ready dan kemudian saya tidak perlu khawatir tentang pengikatan ketika saya membuat HTML. Saya mencari perilaku yang sama dengan DoSomething.
frenchie
Anda dapat menggunakan $ ("div / your-selector"). hasClass ("MyClass"); untuk memeriksa apakah div memiliki kelas tertentu atau tidak. Saya kira Anda bisa menggunakan api ini untuk Anda gunakan.
KBN
.on () hanya untuk acara. Anda dapat menyiapkan peristiwa khusus, tetapi Anda masih harus memicu peristiwa itu ketika Anda membuat elemen baru.
Surreal Dreams
3
Duplikat Acara ketika elemen ditambahkan ke halaman
Dan Dascalescu

Jawaban:

103

Sebelumnya seseorang dapat menghubungkan ke domManipmetode jQuery untuk menangkap semua manipulasi dom jQuery dan melihat elemen apa yang dimasukkan, dll. Tetapi tim jQuery menutupnya di jQuery 3.0+ karena umumnya bukan solusi yang baik untuk menghubungkan ke metode jQuery seperti itu, dan mereka ' telah membuatnya sehingga domManipmetode internal tidak lagi tersedia di luar kode inti jQuery.

Peristiwa Mutasi juga telah ditinggalkan, seperti sebelumnya orang dapat melakukan sesuatu seperti itu

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

ini harus dihindari, dan hari ini Pengamat Mutasi harus digunakan sebagai gantinya, yang akan bekerja seperti ini

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
sumber
Firefox dan webkit seharusnya cukup baik, tetapi dukungan untuk ini agak buruk di IE. Ini harus bekerja di IE9, tapi mungkin tidak begitu banyak di IE8. Saya tahu saya telah melihat solusi, dan saya belum benar-benar menguji ini di IE, jadi uji dulu, jika tidak berfungsi maka lihat apakah ada solusi. Ada sebuah plugin yang sepertinya menambahkan dukungan IE di sini , tetapi belum mencobanya, baru menemukannya di pencarian Google.
adeneo
2
Saya awalnya menggunakan jawaban Anda tetapi fakta bahwa itu tidak berfungsi lintas browser adalah masalah. Kemudian, saya segera menemukan solusi untuk menghindari masalah ini sejak awal. Dan kemudian, beberapa minggu yang lalu masalah pendeteksian penyisipan node ini kembali lagi ke kode saya. Kali ini, kode yang saya posting di jawaban saya bekerja PERSIS seperti yang seharusnya, lintas-browser dan tidak ada buggy. Jadi itulah mengapa saya memindahkan jawaban saya sebagai diterima; begitulah cara SO bekerja, OP memutuskan jawaban mana yang dia terima dan kerumunan memberikan suara pada jawaban. Seiring waktu, orang akan memilih dan memutuskan jawaban mana yang paling cocok untuk mereka.
frenchie
2
Dan juga, saya ingin mengatakan bahwa Anda telah membantu saya beberapa kali dengan jawaban berkualitas tinggi yang menyelamatkan hari saya. Mengubah jawaban yang diterima menjadi jawaban saya murni berdasarkan kode dan bagaimana jawaban itu sesuai dengan masalah yang harus saya selesaikan.
frenchie
mendapat "Uncaught TypeError: Tidak dapat membaca properti 'berisi' tidak terdefinisi" dengan kode ini
gordie
53

Ini adalah plugin saya yang melakukannya - jquery.initialize

Penggunaannya sama seperti Anda menggunakan .eachfungsi, tetapi dengan .initializefungsi pada elemen, perbedaan dari .eachitu juga akan menginisialisasi elemen yang ditambahkan di masa mendatang tanpa kode tambahan - tidak peduli jika Anda menambahkannya dengan AJAX atau yang lainnya.

Inisialisasi memiliki sintaks yang sama persis dengan fungsi .each

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

Tetapi sekarang jika elemen baru yang cocok dengan .some-element selector akan muncul di halaman, itu akan langsung diinisialisasi. Cara item baru ditambahkan tidak penting, Anda tidak perlu peduli dengan callback, dll.

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

Plugin didasarkan pada MutationObserver

pie6k.dll
sumber
Terima kasih banyak.
Brandon Harris
bagaimana cara menghentikan inisialisasi? Saya ingin berhenti menonton setelah barang yang saya cari ditambahkan ...
Evgeny Vostok
$ (". some-element"). initialize [..] tidak digunakan lagi. Gunakan $ .initialize (". AddDialog", function () {$ ('. NoData'). Hide ();}); sebagai gantinya.
Heimi
Apakah mungkin untuk menjalankan .initializefungsi callback hanya untuk elemen yang ditambahkan secara dinamis (misalnya melalui Ajax), dan melewati callback untuk elemen yang sudah ada saat halaman pertama kali dimuat? Saya mencoba menambahkan parameter ke optionsuntuk mencapai ini, tetapi sejauh ini saya belum berhasil.
Nevermore
Anda dapat menambahkan masalah di github tentang itu. Untuk saat ini, secara teknis Anda bisa melakukannya$('.myClass').addClass('ignore-initialize'); $.initialize('.myClass:not(.ignore-initialize)', callback);
pie6k
19

Setelah meninjau ini dan beberapa posting lainnya, saya mencoba menyaring apa yang saya anggap terbaik dari masing-masing menjadi sesuatu yang sederhana yang memungkinkan saya untuk mendeteksi ketika kelas elemen dimasukkan dan kemudian bertindak atas elemen tersebut .

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

Yang penting bagi saya, ini memungkinkan a) elemen target ada pada kedalaman apa pun dalam "addedNodes" dan b) kemampuan untuk menangani elemen hanya saat pertama kali dimasukkan (tidak perlu mencari seluruh pengaturan dokumen atau mengabaikan "sudah diproses" bendera).

John Clegg
sumber
1
Kondisi Anda harus if(mutation.addedNodes.length)karena addedNodesdan removedNodesarray selalu ada di mutationtipe childList(diperiksa di IE11, Chrome53, FF49). Masih +1, karena jawaban Anda adalah satu-satunya jawaban addedNodesyang dianggap ( callbackdipanggil hanya jika node ditambahkan); tidak hanya di utas ini tetapi banyak utas lainnya. Semua orang hanya memanggil callbackuntuk semua mutasi, bahkan jika node sedang dihapus.
Vivek Athalye
17

Pengalaman 3 tahun kemudian, inilah cara saya mendengarkan "elemen kelas tertentu ditambahkan ke DOM" : Anda cukup menambahkan pengait ke dalam html()fungsi jQuery , seperti ini:

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

Ini berfungsi jika Anda menggunakan jQuery, yang saya lakukan. Dan itu tidak bergantung pada acara khusus browser DOMNodeInserted, yang tidak kompatibel lintas-browser. Saya juga menambahkan implementasi yang sama untuk.prepend()

Secara keseluruhan, ini berfungsi seperti pesona bagi saya, dan mudah-mudahan juga untuk Anda.

frenchie
sumber
6
Hmm - Saya akan mengklarifikasi bahwa ini berfungsi jika Anda secara eksklusif menggunakan htmlhelper jQuery untuk memodifikasi DOM, tidak hanya "menggunakan jQuery" secara umum. Elemen yang ditambahkan menggunakan, katakanlah, jQuery.appendtidak akan memicu acara ini.
iameli
1
Ya, dan itulah mengapa saya mengklarifikasi bahwa saya menambahkan implementasi yang sama dengan .preprend () dan jika Anda menggunakan .append () maka lakukan hal yang sama dengan .append ()
frenchie
Ada pendekatan yang jauh lebih cepat, menggunakan animasi CSS3 . Pustaka sumber terbuka juga memberikan kode yang jauh lebih sederhana:insertionQ('#intercom-container').every(function(/element){ element.style.display = 'none'; });
Dan Dascalescu
3
Mengapa downvote ?? Ini benar-benar berfungsi: tidak ada plugin dan tidak ada fungsi setTimeout hacky.
frenchie
2
Suara positif - ini adalah opsi yang valid - mengapa di dunia (!!!) ini telah diturunkan suara ?????? Dan, Animasi CSS3 bukan XBC: caniuse.com/#search=keyframes ; davidwalsh.name/detect-node-insertion .
Cody
6

Anda bisa menggunakan peristiwa mutasi

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

EDIT

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

Tidak Berlaku Lagi Fitur ini telah dihapus dari Web. Meskipun beberapa browser mungkin masih mendukungnya, ini sedang dalam proses pelepasan. Jangan gunakan di proyek lama atau baru. Halaman atau aplikasi Web yang menggunakannya dapat rusak kapan saja.

Mutation Observers adalah pengganti yang diusulkan untuk kejadian mutasi di DOM4. Mereka akan disertakan dalam Firefox 14 dan Chrome 18.

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

MutationObservermemberi pengembang cara untuk bereaksi terhadap perubahan di DOM. Ini dirancang sebagai pengganti Peristiwa Mutasi yang ditentukan dalam spesifikasi Peristiwa DOM3.

Contoh penggunaan

Contoh berikut diambil dari 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
sumber
2
Peristiwa mutasi cukup banyak ditinggalkan menurut mdn , mendukung MutationObservers
Olga
Sekarang IE10 adalah EOL, seharusnya hampir aman untuk mengatakan MutationObserver didukung secara luas.
rymo
0

Dua tambahan jawaban adeneo:

(1) kita dapat mengubah garis

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

Untuk

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

Jadi kami tidak akan melihat "Uncaught TypeError: Cannot read property 'contains' of undefined"kesalahannya.

(2) add subtree: truedi configuntuk menemukan node ditambahkan secara rekursif dalam semua elemen menambahkan.

agrul
sumber