Apakah ada pendengar perubahan JavaScript / jQuery DOM?

402

Intinya saya ingin skrip dieksekusi ketika isi suatu DIVperubahan. Karena skrip terpisah (skrip konten dalam ekstensi Chrome & skrip halaman web), saya perlu cara mengamati perubahan dalam status DOM. Saya bisa mengatur pemungutan suara tetapi itu tampaknya ceroboh.

Fletcher Moore
sumber

Jawaban:

488

Untuk waktu yang lama, peristiwa mutasi DOM3 adalah solusi terbaik yang tersedia, tetapi mereka telah ditinggalkan karena alasan kinerja. Pengamat Mutasi DOM4 adalah pengganti untuk peristiwa mutasi DOM3 yang sudah usang. Mereka saat ini diimplementasikan di browser modern sebagai MutationObserver(atau sebagai awalan vendor WebKitMutationObserverdalam versi lama Chrome):

MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

var observer = new MutationObserver(function(mutations, observer) {
    // fired when a mutation occurs
    console.log(mutations, observer);
    // ...
});

// define what element should be observed by the observer
// and what types of mutations trigger the callback
observer.observe(document, {
  subtree: true,
  attributes: true
  //...
});

Contoh ini mendengarkan perubahan DOM documentdan seluruh subtree-nya, dan itu akan diaktifkan pada perubahan atribut elemen serta perubahan struktural. Draf spesifikasi memiliki daftar lengkap properti pendengar mutasi yang valid :

childList

  • Tetapkan truejika mutasi ke anak-anak target harus diamati.

atribut

  • Setel ke trueapakah mutasi ke atribut target harus diamati.

characterData

  • Setel ke trueapakah mutasi ke data target harus diamati.

subtree

  • Tetapkan truejika mutasi tidak hanya untuk target, tetapi juga keturunan target harus diperhatikan.

atributOldValue

  • Setel ke truejika attributesdisetel ke true dan nilai atribut target sebelum mutasi perlu direkam.

characterDataOldValue

  • Setel ke truejika characterDatadisetel ke true dan data target sebelum mutasi perlu direkam.

atributFilter

  • Setel ke daftar nama lokal atribut (tanpa namespace) jika tidak semua mutasi atribut perlu diperhatikan.

(Daftar ini saat ini per April 2014; Anda dapat memeriksa spesifikasi untuk setiap perubahan.)

apsillers
sumber
3
@AshrafBashir Saya melihat sampel berfungsi dengan baik di Firefox 19.0.2: Saya melihat ([{}])masuk ke konsol, yang menunjukkan yang diharapkan MutationRecordketika saya mengkliknya. Silakan periksa lagi, karena ini mungkin merupakan kegagalan teknis sementara di JSFiddle. Saya belum mengujinya di IE, karena saya tidak punya IE 10, yang saat ini satu-satunya versi untuk mendukung peristiwa mutasi.
apsillers
1
Saya baru saja memposting jawaban yang berfungsi di IE10 + dan kebanyakan hal lain.
naugtur
1
Spesifikasi tampaknya tidak memiliki kotak hijau yang mencantumkan opsi pengamat mutasi lebih lama. Itu daftar opsi di bagian 5.3.1 dan menjelaskan mereka hanya sedikit lebih jauh di bawah itu.
LS
2
@LS Terima kasih, saya telah memperbarui tautan, menghapus sedikit tentang kotak hijau, dan mengedit seluruh daftar menjadi jawaban saya (kalau-kalau membusuk tautan di masa depan).
apsillers
211

Edit

Jawaban ini sekarang sudah usang. Lihat jawabannya oleh apsillers .

Karena ini untuk ekstensi Chrome, Anda sebaiknya menggunakan acara DOM standar - DOMSubtreeModified. Lihat dukungan untuk acara ini di seluruh browser. Itu telah didukung di Chrome sejak 1.0.

$("#someDiv").bind("DOMSubtreeModified", function() {
    alert("tree changed");
});

Lihat contoh yang berfungsi di sini .

Anurag
sumber
Saya perhatikan bahwa acara ini dapat dipecat bahkan setelah penyeleksi tertentu. Saya sedang menyelidiki.
Peder Rice
9
w3.org/TR/DOM-Level-3-Events/#event-type-DOMSubtreeModified mengatakan acara ini sudah usang, apa yang akan kita gunakan sebagai gantinya?
Maslow
2
@ Maslow- Tidak ada! stackoverflow.com/questions/6659662/…
Sam Rueby
4
Ada github.com/joelpurra/jquery-mutation-summary yang pada dasarnya memecahkannya untuk pengguna jquery.
Capi Etheriel
1
Masih berfungsi jika Anda sedang membangun ekstensi chrome. tak ternilai.
concept47
49

Banyak situs menggunakan AJAX untuk menambah / menampilkan / mengubah konten secara dinamis. Kadang-kadang digunakan alih-alih navigasi di dalam situs, sehingga URL saat ini diubah secara terprogram dan skrip konten tidak secara otomatis dijalankan oleh browser dalam hal ini karena halaman tersebut tidak diambil dari server jauh sepenuhnya.


Metode JS biasa mendeteksi perubahan halaman yang tersedia dalam skrip konten .

  • MutationObserver ( docs ) untuk mendeteksi perubahan DOM:

  • Pendengar acara untuk situs yang memberi sinyal perubahan konten dengan mengirim acara DOM:

  • Pengecekan DOM secara berkala melalui setInterval :
    Jelas ini hanya akan berfungsi jika Anda menunggu elemen tertentu yang diidentifikasi oleh id / pemilihnya muncul, dan itu tidak akan membiarkan Anda secara universal mendeteksi konten yang ditambahkan secara dinamis baru kecuali jika Anda menemukan semacam sidik jari isi yang ada.

  • Cloaking History API di dalam skrip DOM yang disuntikkan :

    document.head.appendChild(document.createElement('script')).text = '(' +
        function() {
            // injected DOM script is not a content script anymore, 
            // it can modify objects and functions of the page
            var _pushState = history.pushState;
            history.pushState = function(state, title, url) {
                _pushState.call(this, state, title, url);
                window.dispatchEvent(new CustomEvent('state-changed', {detail: state}));
            };
            // repeat the above for replaceState too
        } + ')(); this.remove();'; // remove the DOM script element
    
    // And here content script listens to our DOM script custom events
    window.addEventListener('state-changed', function(e) {
        console.log('History state changed', e.detail, location.hash);
        doSomething();
    });
  • Mendengarkan hashchange , popstate events:

    window.addEventListener('hashchange', function(e) {
        console.log('URL hash changed', e);
        doSomething();
    });
    window.addEventListener('popstate', function(e) {
        console.log('State changed', e);
        doSomething();
    });



Khusus ekstensi: mendeteksi perubahan URL di latar belakang / halaman acara .

Ada API canggih untuk bekerja dengan navigasi: navigasi web , webRequest , tetapi kami akan menggunakan chrome.tabs.onUpdated pendengar acara sederhana yang mengirim pesan ke skrip konten:

  • manifest.json:
    menyatakan latar belakang / halaman acara
    menyatakan skrip konten
    menambahkan "tabs" izin .

  • background.js

    var rxLookfor = /^https?:\/\/(www\.)?google\.(com|\w\w(\.\w\w)?)\/.*?[?#&]q=/;
    chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
        if (rxLookfor.test(changeInfo.url)) {
            chrome.tabs.sendMessage(tabId, 'url-update');
        }
    });
  • content.js

    chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
        if (msg === 'url-update') {
            doSomething();
        }
    });
wOxxOm
sumber
29

Pendekatan lain tergantung pada bagaimana Anda mengubah div. Jika Anda menggunakan JQuery untuk mengubah konten div dengan metode html (), Anda dapat memperluas metode itu dan memanggil fungsi registrasi setiap kali Anda memasukkan html ke div.

(function( $, oldHtmlMethod ){
    // Override the core html method in the jQuery object.
    $.fn.html = function(){
        // Execute the original HTML method using the
        // augmented arguments collection.

        var results = oldHtmlMethod.apply( this, arguments );
        com.invisibility.elements.findAndRegisterElements(this);
        return results;

    };
})( jQuery, jQuery.fn.html );

Kami hanya mencegat panggilan ke html (), memanggil fungsi pendaftaran dengan ini, yang dalam konteksnya merujuk pada elemen target mendapatkan konten baru, kemudian kami meneruskan panggilan ke fungsi jquery.html () asli. Ingatlah untuk mengembalikan hasil metode html () asli, karena JQuery mengharapkannya untuk metode chaining.

Untuk info lebih lanjut tentang metode overriding dan ekstensi, lihat http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm , di mana Saya menuliskan fungsi penutupan. Lihat juga tutorial plugin di situs JQuery.

Zac Imboden
sumber
7

Selain alat "mentah" yang disediakan oleh MutationObserverAPI , ada pustaka "kenyamanan" untuk bekerja dengan mutasi DOM.

Pertimbangkan: MutationObserver mewakili setiap perubahan DOM dalam hal subtrees. Jadi, jika Anda, misalnya, menunggu elemen tertentu dimasukkan, itu mungkin jauh di dalam diri anak-anakmutations.mutation[i].addedNodes[j] .

Masalah lain adalah ketika kode Anda sendiri, sebagai reaksi terhadap mutasi, mengubah DOM - Anda sering ingin memfilternya.

Perpustakaan kenyamanan yang baik yang memecahkan masalah tersebut adalah mutation-summary (penafian: Saya bukan pembuatnya, hanya pengguna yang puas), yang memungkinkan Anda menentukan pertanyaan dari apa yang Anda minati, dan mendapatkan persisnya.

Contoh penggunaan dasar dari dokumen:

var observer = new MutationSummary({
  callback: updateWidgets,
  queries: [{
    element: '[data-widget]'
  }]
});

function updateWidgets(summaries) {
  var widgetSummary = summaries[0];
  widgetSummary.added.forEach(buildNewWidget);
  widgetSummary.removed.forEach(cleanupExistingWidget);
}
Xan
sumber