MutationObserver bu DOM elementini kuzatuvchi va ozgarish aniqlanganida callback ishga tushiruvchi ichki obyekt.
Avval sintaksisga qarab, keyin real foydalanish misolini organamiz va bunday narsa qayerda foydali bolishi mumkinligini koramiz.
Sintaksis
MutationObserver dan foydalanish oson.
Birinchi, biz callback-funksiya bilan kuzatuvchi yaratamiz:
let observer = new MutationObserver(callback);
Songra uni DOM tuguniga boglaymiz:
observer.observe(node, config);
config bu qanday ozgarishlarga javob berish haqidagi boolean variantlar bilan obyekt:
childListnodening bevosita bolalarida ozgarishlar,subtreenodening barcha avlodlarida,attributesnodening atributlarida,attributeFilteratribut nomlari massivi, faqat tanlanganlarini kuzatish uchun.characterDatanode.datani (matn mazmuni) kuzatish kerakmi,
Boshqa variantlar:
attributeOldValueagartruebolsa, atributning eski va yangi qiymatini callback ga uzatadi (quyida koring), aks holda faqat yangisini (attributesvarianti kerak),characterDataOldValueagartruebolsa,node.dataning eski va yangi qiymatini callback ga uzatadi (quyida koring), aks holda faqat yangisini (characterDatavarianti kerak).
Keyin har qanday ozgarishlardan song callback bajariladi: ozgarishlar birinchi argumentda MutationRecord obyektlari royxati sifatida, kuzatuvchining ozi ikkinchi argument sifatida uzatiladi.
MutationRecord obyektlari quyidagi xususiyatlarga ega:
typemutatsiya turi, quyidagilardan biri:"attributes": atribut ozgartirildi"characterData": malumot ozgartirildi, matn tugunlari uchun ishlatiladi,"childList": bola elementlar qoshildi/ochirildi,
targetozgarish qayerda sodir boldi:"attributes"uchun element,"characterData"uchun matn tuguni yoki"childList"mutatsiyasi uchun element,addedNodes/removedNodesqoshilgan/ochirilgan tugunlar,previousSibling/nextSiblingqoshilgan/ochirilgan tugunlarning oldingi va keyingi qoshnilari,attributeName/attributeNamespaceozgartirilgan atributning nomi/nomlar maydoni (XML uchun),oldValueoldingi qiymat, faqat atribut yoki matn ozgarishlari uchun, agar mos variant ornatilgan bolsaattributeOldValue/characterDataOldValue.
Masalan, mana contentEditable atributli <div>. Bu atribut bizga unga etibor qaratish va tahrirlash imkonini beradi.
<div contentEditable id="elem">Bosing va <b>tahrirlang</b>, iltimos</div>
<script>
let observer = new MutationObserver(mutationRecords => {
console.log(mutationRecords); // console.log(o'zgarishlar)
});
// atributlardan tashqari hammani kuzat
observer.observe(elem, {
childList: true, // bevosita bolalarni kuzat
subtree: true, // pastki avlodlarni ham
characterDataOldValue: true // eski ma'lumotni callback ga uzat
});
</script>
Agar biz bu kodni brauzerda ishga tushirib, berilgan <div> ga etibor qaratib, <b>tahrirlang</b> ichidagi matnni ozgartirsak, console.log bitta mutatsiyani korsatadi:
mutationRecords = [{
type: "characterData",
oldValue: "tahrirlang",
target: <text node>,
// boshqa xususiyatlar bo'sh
}];
Agar biz murakkabroq tahrirlash operatsiyalarini bajarsak, masalan <b>tahrirlang</b> ni ochirsak, mutatsiya hodisasi bir nechta mutatsiya yozuvlarini oz ichiga olishi mumkin:
mutationRecords = [{
type: "childList",
target: <div#elem>,
removedNodes: [<b>],
nextSibling: <text node>,
previousSibling: <text node>
// boshqa xususiyatlar bo'sh
}, {
type: "characterData"
target: <text node>
// ...mutatsiya tafsilotlari brauzerning bunday o'chirishni qanday hal qilishiga bog'liq
// u ikki qo'shni matn tugunlarini "tahrirlang " va ", iltimos" ni bitta tugunga birlashtirishi mumkin
// yoki ularni alohida matn tugunlari sifatida qoldirishi mumkin
}];
Demak, MutationObserver DOM pastdaraxtidagi har qanday ozgarishlarga javob berish imkonini beradi.
Integratsiya uchun foydalanish
Bunday narsa qachon foydali bolishi mumkin?
Foydali funksiyani oz ichiga olgan, lekin ayni paytda keraksiz narsalarni, masalan reklamalarni <div class="ads">Keraksiz reklamalar</div> korsatadigan uchinchi tomon skriptini qoshishingiz kerak bolgan vaziyatni tasavvur qiling.
Tabiiyki, uchinchi tomon skripti uni ochirish uchun hech qanday mexanizm bermaydi.
MutationObserver dan foydalanib, keraksiz element bizning DOM da paydo bolganini aniqlab, uni ochirishimiz mumkin.
Uchinchi tomon skripti bizning hujjatimizga biror narsa qoshadigan va biz buni aniqlamoqchi bolgan, sahifamizni moslashtiramiz, dinamik ravishda biror narsani olchamini ozgartiramiz va hokazolar qiladigan boshqa vaziyatlar ham bor.
MutationObserver buni amalga oshirish imkonini beradi.
Arxitektura uchun foydalanish
MutationObserver arxitektura nuqtayi nazaridan yaxshi bolgan vaziyatlar ham bor.
Aytaylik, biz dasturlash haqida veb-sayt yaratyapmiz. Tabiiyki, maqolalar va boshqa materiallar kod qisqichlarini oz ichiga olishi mumkin.
HTML belgilashda bunday qisqich quyidagicha korinadi:
...
<pre class="language-javascript"><code>
// mana kod
let hello = "world";
</code></pre>
...
Yaxshiroq oqilishi va ayni paytda uni chiroylashtiramiz uchun saytimizda JavaScript sintaksis takidlash kutubxonasidan, masalan Prism.js dan foydalanamiz. Yuqoridagi qisqich uchun Prism da sintaksis takidlashni olish uchun Prism.highlightElem(pre) chaqiriladi, bu bunday pre elementlarining mazmunini tekshiradi va rangli sintaksis takidlash uchun maxsus teglar va stillarni qoshadi, xuddi bu sahifadagi misollarda korganingizdek.
Aynan qachon bu takidlash usulini ishga tushirishimiz kerak? Biz buni DOMContentLoaded hodisasida qilishimiz yoki skriptni sahifaning pastki qismiga qoyishimiz mumkin. DOM tayyor bolgan paytda biz pre[class*="language"] elementlarini qidiribo, ularga Prism.highlightElem chaqirishimiz mumkin:
// sahifadagi barcha kod qisqichlarini ta'kidlash
document.querySelectorAll('pre[class*="language"]').forEach(Prism.highlightElem);
Hozircha hammasi oddiy, shunday emasmi? Biz HTML da kod qisqichlarini topamiz va ularni takidlaymiz.
Endi davom etaylik. Aytaylik, biz serverdan dinamik ravishda materiallarni yuklamoqchimiz. Buning usullarini keyinroq organamiz. Hozir faqat veb-serverdan HTML maqola yuklab, uni talab boyicha korsatishimiz muhim:
let article = /* serverdan yangi kontent yuklash */
articleElem.innerHTML = article;
Yangi article HTML kod qisqichlarini oz ichiga olishi mumkin. Ularga Prism.highlightElem chaqirishimiz kerak, aks holda ular takidlanmaydi.
Dinamik yuklangan maqola uchun Prism.highlightElem ni qayerda va qachon chaqirish kerak?
Biz bu chaqiruvni maqola yuklaydigan kodga qoshishimiz mumkin:
let article = /* serverdan yangi kontent yuklash */
articleElem.innerHTML = article;
let snippets = articleElem.querySelectorAll('pre[class*="language-"]');
snippets.forEach(Prism.highlightElem);
Lekin tasavvur qiling, kodda kontent yuklaydigan kop joylar bor maqolalar, viktorinalar, forum postlari va hokazo. Takidlash chaqiruvini har yerga qoyishimiz kerakmi, yuklagandan keyin kontentdagi kodni takidlash uchun? Bu unchalik qulay emas.
Va agar kontent uchinchi tomon moduli tomonidan yuklanayotgan bolsa-chi? Masalan, bizda kimdir tomonidan yozilgan, kontentni dinamik yuklaydigaan forum bor va biz unga sintaksis takidlashni qoshmoqchimiz. Hech kim uchinchi tomon skriptlarini yamoqlashni yoqtirmaydi.
Yaxshiyamki, boshqa variant bor.
Biz kod qisqichlari sahifaga qoshilganini avtomatik aniqlash va ularni takidlash uchun MutationObserver dan foydalanishimiz mumkin.
Shunday qilib biz takidlash funksiyasini bir joyda boshqaramiz, uni integratsiya qilish ehtiyojidan xalos bolamiz.
Dinamik takidlash namoyishi
Mana ishlaydigan misol.
Agar siz bu kodni ishga tushirsangiz, u quyidagi elementni kuzata boshlaydi va u yerda paydo boladigan har qanday kod qisqichlarini takidlaydi:
let observer = new MutationObserver(mutations => {
for(let mutation of mutations) {
// yangi tugunlarni tekshir, ta'kidlanadigan narsa bormi?
for(let node of mutation.addedNodes) {
// faqat elementlarni kuzatamiz, boshqa tugunlarni (masalan matn tugunlarini) o'tkazib yuboramiz
if (!(node instanceof HTMLElement)) continue;
// qo'shilgan elementni kod qisqichi ekanligini tekshir
if (node.matches('pre[class*="language-"]')) {
Prism.highlightElement(node);
}
// yoki uning pastdaraxtida kod qisqichi bormi?
for(let elem of node.querySelectorAll('pre[class*="language-"]')) {
Prism.highlightElement(elem);
}
}
}
});
let demoElem = document.getElementById('highlight-demo');
observer.observe(demoElem, {childList: true, subtree: true});
Bu yerda, pastda HTML-element va uni innerHTML yordamida dinamik toldiradigan JavaScript bor.
Iltimos, oldingi kodni ishga tushiring (yuqorida, u elementni kuzatadi), keyin quyidagi kodni. MutationObserver qisqichni qanday aniqlab, takidlashini korasiz.
Quyidagi kod uning innerHTML ni toldiradi, bu MutationObserver ning javob berishiga va mazmunini takidlashiga sabab boladi:
let demoElem = document.getElementById('highlight-demo');
// kod qisqichlari bilan kontentni dinamik qo'shish
demoElem.innerHTML = `Quyida kod qisqichi bor:
<pre class="language-javascript"><code> let hello = "world!"; </code></pre>
<div>Yana biri:</div>
<div>
<pre class="language-css"><code>.class { margin: 5px; } </code></pre>
</div>
`;
Endi bizda kuzatiladigan elementlar yoki butun document da barcha takidlashlarni kuzata oladigan MutationObserver bor. Biz bu haqida oylamasdan HTML da kod qisqichlarini qoshish/ochirish mumkin.
Qoshimcha usullar
Tugunni kuzatishni toxtatish usuli mavjud:
observer.disconnect()kuzatishni toxtatadi.
Kuzatishni toxtatganimizda, bazi ozgarishlar hali kuzatuvchi tomonidan qayta ishlanmagan bolishi mumkin. Bunday hollarda biz quyidagini ishlatamiz:
observer.takeRecords()qayta ishlanmagan mutatsiya yozuvlari royxatini oladi sodir bolgan, lekin callback ularni hal qilmagan.
Bu usullarni birgalikda ishlatish mumkin:
// qayta ishlanmagan mutatsiyalar ro'yxatini olish
// uzishdan oldin chaqirilishi kerak,
// agar yaqinda hal qilinmagan mutatsiyalar haqida qayg'ursangiz
let mutationRecords = observer.takeRecords();
// o'zgarishlarni kuzatishni to'xtat
observer.disconnect();
...
observer.takeRecords() tomonidan qaytarilgan yozuvlar qayta ishlash navbatidan ochiriladi
Callback observer.takeRecords() tomonidan qaytarilgan yozuvlar uchun chaqirilmaydi.
Axlat yigish bilan ozaro tasir Kuzatuvchilar ichkarida tugunlarga zaif havolalardan foydalanadi. Yani, agar tugun DOM dan ochirilsa va erishib bolmaydigan bolsa, u axlat yigilishi mumkin.
DOM tuguni kuzatilayotgani axlat yigishning oldini olmaydi.
Xulosa
MutationObserver DOM dagi ozgarishlarga javob bera oladi atributlar, matn mazmuni va elementlar qoshish/ochirish.
Biz uni kodimizning boshqa qismlari tomonidan kiritilgan ozgarishlarni kuzatish, shuningdek uchinchi tomon skriptlari bilan integratsiya qilish uchun ishlatishimiz mumkin.
MutationObserver har qanday ozgarishlarni kuzata oladi. Nimani kuzatish konfiguratsiya variantlari keraksiz callback chaqiruvlariga resurs sarflamaslik uchun optimallashtirishlar uchun ishlatiladi.
Izohlar
<code>yorlig'ini ishlating, bir nechta satrlar uchun - ularni<pre>yorlig'i bilan o'rab qo'ying, 10 satrdan ortiq bo'lsa - sandbox (plnkr, jsbin, codepen)