Saya ingin fungsi yang saya pilih berjalan saat elemen DOM ditambahkan ke halaman. Ini dalam konteks ekstensi browser, jadi halaman web berjalan secara independen dari saya dan saya tidak dapat mengubah sumbernya. Apa saja pilihan saya di sini?
Saya rasa, secara teori, saya dapat menggunakan setInterval()
untuk terus mencari keberadaan elemen dan melakukan tindakan saya jika elemen tersebut ada, tetapi saya membutuhkan pendekatan yang lebih baik.
Jawaban:
Karena peristiwa mutasi tidak digunakan lagi pada tahun 2012, dan Anda tidak memiliki kontrol atas elemen yang disisipkan karena ditambahkan oleh kode orang lain, satu-satunya pilihan Anda adalah terus memeriksanya.
function checkDOMChange() { // check for any new element being inserted here, // or a particular node being modified // call the function again after 100 milliseconds setTimeout( checkDOMChange, 100 ); }
Setelah dipanggil, fungsi ini akan berjalan setiap 100 milidetik, yaitu 1/10 (sepersepuluh) detik. Kecuali jika Anda memerlukan observasi elemen secara real-time, itu sudah cukup.
sumber
MutationObserver()
: davidwalsh.name/mutationobserver-apiEvent.ADDED_TO_STAGE
?Jawaban sebenarnya adalah "gunakan pengamat mutasi" (seperti yang dijelaskan dalam pertanyaan ini: Menentukan apakah elemen HTML telah ditambahkan ke DOM secara dinamis ), namun dukungan (khususnya di IE) terbatas ( http://caniuse.com/mutationobserver ) .
Jadi jawaban SEBENARNYA yang sebenarnya adalah "Gunakan pengamat mutasi .... akhirnya. Tapi ikuti jawaban Jose Faeti untuk saat ini" :)
sumber
Antara penghentian peristiwa mutasi dan kemunculannya
MutationObserver
, cara efektif untuk diberi tahu saat elemen tertentu ditambahkan ke DOM adalah dengan mengeksploitasi peristiwa animasi CSS3 .Mengutip postingan blog:
sumber
animationstart
, jsbin.com/netuquralu/1/edit .Anda dapat menggunakan
livequery
plugin untuk jQuery. Anda dapat memberikan ekspresi pemilih seperti:$("input[type=button].removeItemButton").livequery(function () { $("#statusBar").text('You may now remove items.'); });
Setiap kali tombol a
removeItemButton
kelas ditambahkan, pesan muncul di bilah status.Dalam hal efisiensi, Anda mungkin ingin menghindari ini, tetapi dalam hal apa pun Anda dapat memanfaatkan plugin daripada membuat penangan acara Anda sendiri.
Jawaban yang ditinjau kembali
Jawaban di atas hanya untuk mendeteksi bahwa suatu item telah ditambahkan ke DOM melalui plugin.
Namun, kemungkinan besar,
jQuery.on()
pendekatan akan lebih tepat, misalnya:$("#myParentContainer").on('click', '.removeItemButton', function(){ alert($(this).text() + ' has been removed'); });
Misalnya, jika Anda memiliki konten dinamis yang harus merespons klik, sebaiknya ikat peristiwa ke penampung induk menggunakan
jQuery.on
.sumber
ETA 24 Apr 17 Saya ingin menyederhanakan ini sedikit dengan beberapa
async
/await
magic, karena membuatnya jauh lebih ringkas:Menggunakan promisified-observable yang sama:
const startObservable = (domNode) => { var targetNode = domNode; var observerConfig = { attributes: true, childList: true, characterData: true }; return new Promise((resolve) => { var observer = new MutationObserver(function (mutations) { // For the sake of...observation...let's output the mutation to console to see how this all works mutations.forEach(function (mutation) { console.log(mutation.type); }); resolve(mutations) }); observer.observe(targetNode, observerConfig); }) }
Fungsi panggilan Anda bisa sesederhana:
const waitForMutation = async () => { const button = document.querySelector('.some-button') if (button !== null) button.click() try { const results = await startObservable(someDomNode) return results } catch (err) { console.error(err) } }
Jika Anda ingin menambahkan batas waktu, Anda dapat menggunakan
Promise.race
pola sederhana seperti yang ditunjukkan di sini :const waitForMutation = async (timeout = 5000 /*in ms*/) => { const button = document.querySelector('.some-button') if (button !== null) button.click() try { const results = await Promise.race([ startObservable(someDomNode), // this will throw after the timeout, skipping // the return & going to the catch block new Promise((resolve, reject) => setTimeout( reject, timeout, new Error('timed out waiting for mutation') ) ]) return results } catch (err) { console.error(err) } }
Asli
Anda dapat melakukan ini tanpa perpustakaan, tetapi Anda harus menggunakan beberapa barang ES6, jadi waspadalah terhadap masalah kompatibilitas (yaitu, jika audiens Anda sebagian besar adalah Amish, luddite atau, lebih buruk, pengguna IE8)
Pertama, kita akan menggunakan API MutationObserver untuk membuat objek pengamat. Kami akan membungkus objek ini dalam sebuah promise, dan
resolve()
saat callback diaktifkan (h / t davidwalshblog) artikel blog david walsh tentang mutasi :const startObservable = (domNode) => { var targetNode = domNode; var observerConfig = { attributes: true, childList: true, characterData: true }; return new Promise((resolve) => { var observer = new MutationObserver(function (mutations) { // For the sake of...observation...let's output the mutation to console to see how this all works mutations.forEach(function (mutation) { console.log(mutation.type); }); resolve(mutations) }); observer.observe(targetNode, observerConfig); }) }
Kemudian, kami akan membuat file
generator function
. Jika Anda belum menggunakan ini, maka Anda ketinggalan - tetapi sinopsis singkatnya adalah: ini berjalan seperti fungsi sinkronisasi, dan ketika menemukanyield <Promise>
ekspresi, itu menunggu dengan cara yang tidak menghalangi janji itu akan terjadi. terpenuhi ( Generator melakukan lebih dari ini, tetapi inilah yang kami minati di sini ).// we'll declare our DOM node here, too let targ = document.querySelector('#domNodeToWatch') function* getMutation() { console.log("Starting") var mutations = yield startObservable(targ) console.log("done") }
Bagian rumit tentang generator adalah mereka tidak 'mengembalikan' seperti fungsi normal. Jadi, kita akan menggunakan fungsi pembantu agar bisa menggunakan generator seperti fungsi biasa. (sekali lagi, h / t ke dwb )
function runGenerator(g) { var it = g(), ret; // asynchronously iterate over generator (function iterate(val){ ret = it.next( val ); if (!ret.done) { // poor man's "is it a promise?" test if ("then" in ret.value) { // wait on the promise ret.value.then( iterate ); } // immediate value: just send right back in else { // avoid synchronous recursion setTimeout( function(){ iterate( ret.value ); }, 0 ); } } })(); }
Kemudian, pada titik mana pun sebelum mutasi DOM yang diharapkan mungkin terjadi, jalankan saja
runGenerator(getMutation)
.Sekarang Anda dapat mengintegrasikan mutasi DOM ke dalam aliran kontrol gaya sinkron. Bagaimana kalau itu.
sumber
Ada pustaka javascript yang menjanjikan bernama Tiba yang terlihat seperti cara yang bagus untuk mulai memanfaatkan pengamat mutasi begitu dukungan browser menjadi hal yang biasa.
https://github.com/uzairfarooq/arrive/
sumber
Lihat plugin ini yang benar-benar melakukannya - jquery.initialize
Ini bekerja persis seperti fungsi .each, perbedaannya adalah dibutuhkan pemilih yang Anda masukkan dan perhatikan item baru yang ditambahkan di masa mendatang yang cocok dengan pemilih ini dan menginisialisasi mereka
Inisialisasi terlihat seperti ini
$(".some-element").initialize( function(){ $(this).css("color", "blue"); });
Tetapi sekarang jika
.some-element
pemilih pencocokan elemen baru akan muncul di halaman, itu akan diinisialisasi secara instan.Cara item baru ditambahkan tidak penting, Anda tidak perlu peduli dengan callback, dll.
Jadi jika Anda menambahkan elemen baru seperti:
$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!
itu akan langsung diinisialisasi.
Plugin didasarkan pada
MutationObserver
sumber
Solusi javascript murni (tanpa
jQuery
):const SEARCH_DELAY = 100; // in ms // it may run indefinitely. TODO: make it cancellable, using Promise's `reject` function waitForElementToBeAdded(cssSelector) { return new Promise((resolve) => { const interval = setInterval(() => { if (element = document.querySelector(cssSelector)) { clearInterval(interval); resolve(element); } }, SEARCH_DELAY); }); } console.log(await waitForElementToBeAdded('#main'));
sumber