MutationObserver â bu DOM elementini kuzatuvchi va oâzgarish aniqlanganida callback ishga tushiruvchi ichki obyekt.
Avval sintaksisga qarab, keyin real foydalanish misolini oârganamiz va bunday narsa qayerda foydali boâlishi mumkinligini koâramiz.
Sintaksis
MutationObserver dan foydalanish oson.
Birinchi, biz callback-funksiya bilan kuzatuvchi yaratamiz:
let observer = new MutationObserver(callback);
Soângra uni DOM tuguniga bogâlaymiz:
observer.observe(node, config);
config â bu âqanday oâzgarishlarga javob berishâ haqidagi boolean variantlar bilan obyekt:
childListânodening bevosita bolalarida oâzgarishlar,subtreeânodening barcha avlodlarida,attributesânodening atributlarida,attributeFilterâ atribut nomlari massivi, faqat tanlanganlarini kuzatish uchun.characterDataânode.datani (matn mazmuni) kuzatish kerakmi,
Boshqa variantlar:
attributeOldValueâ agartrueboâlsa, atributning eski va yangi qiymatini callback ga uzatadi (quyida koâring), aks holda faqat yangisini (attributesvarianti kerak),characterDataOldValueâ agartrueboâlsa,node.dataning eski va yangi qiymatini callback ga uzatadi (quyida koâring), aks holda faqat yangisini (characterDatavarianti kerak).
Keyin har qanday oâzgarishlardan soâng callback bajariladi: oâzgarishlar birinchi argumentda MutationRecord obyektlari roâyxati sifatida, kuzatuvchining oâzi ikkinchi argument sifatida uzatiladi.
MutationRecord obyektlari quyidagi xususiyatlarga ega:
typeâ mutatsiya turi, quyidagilardan biri:"attributes": atribut oâzgartirildi"characterData": maâlumot oâzgartirildi, matn tugunlari uchun ishlatiladi,"childList": bola elementlar qoâshildi/oâchirildi,
targetâ oâzgarish qayerda sodir boâldi:"attributes"uchun element,"characterData"uchun matn tuguni yoki"childList"mutatsiyasi uchun element,addedNodes/removedNodesâ qoâshilgan/oâchirilgan tugunlar,previousSibling/nextSiblingâ qoâshilgan/oâchirilgan tugunlarning oldingi va keyingi qoâshnilari,attributeName/attributeNamespaceâ oâzgartirilgan atributning nomi/nomlar maydoni (XML uchun),oldValueâ oldingi qiymat, faqat atribut yoki matn oâzgarishlari uchun, agar mos variant oârnatilgan boâlsaattributeOldValue/characterDataOldValue.
Masalan, mana contentEditable atributli <div>. Bu atribut bizga unga eâtibor 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 eâtibor qaratib, <b>tahrirlang</b> ichidagi matnni oâzgartirsak, console.log bitta mutatsiyani koârsatadi:
mutationRecords = [{
type: "characterData",
oldValue: "tahrirlang",
target: <text node>,
// boshqa xususiyatlar bo'sh
}];
Agar biz murakkabroq tahrirlash operatsiyalarini bajarsak, masalan <b>tahrirlang</b> ni oâchirsak, mutatsiya hodisasi bir nechta mutatsiya yozuvlarini oâz 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 oâzgarishlarga javob berish imkonini beradi.
Integratsiya uchun foydalanish
Bunday narsa qachon foydali boâlishi mumkin?
Foydali funksiyani oâz ichiga olgan, lekin ayni paytda keraksiz narsalarni, masalan reklamalarni <div class="ads">Keraksiz reklamalar</div> koârsatadigan uchinchi tomon skriptini qoâshishingiz kerak boâlgan vaziyatni tasavvur qiling.
Tabiiyki, uchinchi tomon skripti uni oâchirish uchun hech qanday mexanizm bermaydi.
MutationObserver dan foydalanib, keraksiz element bizning DOM da paydo boâlganini aniqlab, uni oâchirishimiz mumkin.
Uchinchi tomon skripti bizning hujjatimizga biror narsa qoâshadigan va biz buni aniqlamoqchi boâlgan, sahifamizni moslashtiramiz, dinamik ravishda biror narsani oâlchamini oâzgartiramiz va hokazolar qiladigan boshqa vaziyatlar ham bor.
MutationObserver buni amalga oshirish imkonini beradi.
Arxitektura uchun foydalanish
MutationObserver arxitektura nuqtayi nazaridan yaxshi boâlgan vaziyatlar ham bor.
Aytaylik, biz dasturlash haqida veb-sayt yaratyapmiz. Tabiiyki, maqolalar va boshqa materiallar kod qisqichlarini oâz ichiga olishi mumkin.
HTML belgilashda bunday qisqich quyidagicha koârinadi:
...
<pre class="language-javascript"><code>
// mana kod
let hello = "world";
</code></pre>
...
Yaxshiroq oâqilishi va ayni paytda uni chiroylashtiramiz uchun saytimizda JavaScript sintaksis taâkidlash kutubxonasidan, masalan Prism.js dan foydalanamiz. Yuqoridagi qisqich uchun Prism da sintaksis taâkidlashni olish uchun Prism.highlightElem(pre) chaqiriladi, bu bunday pre elementlarining mazmunini tekshiradi va rangli sintaksis taâkidlash uchun maxsus teglar va stillarni qoâshadi, xuddi bu sahifadagi misollarda koârganingizdek.
Aynan qachon bu taâkidlash usulini ishga tushirishimiz kerak? Biz buni DOMContentLoaded hodisasida qilishimiz yoki skriptni sahifaning pastki qismiga qoâyishimiz mumkin. DOM tayyor boâlgan 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 taâkidlaymiz.
Endi davom etaylik. Aytaylik, biz serverdan dinamik ravishda materiallarni yuklamoqchimiz. Buning usullarini keyinroq oârganamiz. Hozir faqat veb-serverdan HTML maqola yuklab, uni talab boâyicha koârsatishimiz muhim:
let article = /* serverdan yangi kontent yuklash */
articleElem.innerHTML = article;
Yangi article HTML kod qisqichlarini oâz ichiga olishi mumkin. Ularga Prism.highlightElem chaqirishimiz kerak, aks holda ular taâkidlanmaydi.
Dinamik yuklangan maqola uchun Prism.highlightElem ni qayerda va qachon chaqirish kerak?
Biz bu chaqiruvni maqola yuklaydigan kodga qoâshishimiz 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 koâp joylar bor â maqolalar, viktorinalar, forum postlari va hokazo. Taâkidlash chaqiruvini har yerga qoâyishimiz kerakmi, yuklagandan keyin kontentdagi kodni taâkidlash uchun? Bu unchalik qulay emas.
Va agar kontent uchinchi tomon moduli tomonidan yuklanayotgan boâlsa-chi? Masalan, bizda kimdir tomonidan yozilgan, kontentni dinamik yuklaydigaan forum bor va biz unga sintaksis taâkidlashni qoâshmoqchimiz. Hech kim uchinchi tomon skriptlarini yamoqlashni yoqtirmaydi.
Yaxshiyamki, boshqa variant bor.
Biz kod qisqichlari sahifaga qoâshilganini avtomatik aniqlash va ularni taâkidlash uchun MutationObserver dan foydalanishimiz mumkin.
Shunday qilib biz taâkidlash funksiyasini bir joyda boshqaramiz, uni integratsiya qilish ehtiyojidan xalos boâlamiz.
Dinamik taâkidlash namoyishi
Mana ishlaydigan misol.
Agar siz bu kodni ishga tushirsangiz, u quyidagi elementni kuzata boshlaydi va u yerda paydo boâladigan har qanday kod qisqichlarini taâkidlaydi:
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 toâldiradigan JavaScript bor.
Iltimos, oldingi kodni ishga tushiring (yuqorida, u elementni kuzatadi), keyin quyidagi kodni. MutationObserver qisqichni qanday aniqlab, taâkidlashini koârasiz.
Quyidagi kod uning innerHTML ni toâldiradi, bu MutationObserver ning javob berishiga va mazmunini taâkidlashiga sabab boâladi:
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 taâkidlashlarni kuzata oladigan MutationObserver bor. Biz bu haqida oâylamasdan HTML da kod qisqichlarini qoâshish/oâchirish mumkin.
Qoâshimcha usullar
Tugunni kuzatishni toâxtatish usuli mavjud:
observer.disconnect()â kuzatishni toâxtatadi.
Kuzatishni toâxtatganimizda, baâzi oâzgarishlar hali kuzatuvchi tomonidan qayta ishlanmagan boâlishi mumkin. Bunday hollarda biz quyidagini ishlatamiz:
observer.takeRecords()â qayta ishlanmagan mutatsiya yozuvlari roâyxatini oladi â sodir boâlgan, 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 oâchiriladi
Callback observer.takeRecords() tomonidan qaytarilgan yozuvlar uchun chaqirilmaydi.
Axlat yigâish bilan oâzaro taâsir Kuzatuvchilar ichkarida tugunlarga zaif havolalardan foydalanadi. Yaâni, agar tugun DOM dan oâchirilsa va erishib boâlmaydigan boâlsa, u axlat yigâilishi mumkin.
DOM tuguni kuzatilayotgani axlat yigâishning oldini olmaydi.
Xulosa
MutationObserver DOM dagi oâzgarishlarga javob bera oladi â atributlar, matn mazmuni va elementlar qoâshish/oâchirish.
Biz uni kodimizning boshqa qismlari tomonidan kiritilgan oâzgarishlarni kuzatish, shuningdek uchinchi tomon skriptlari bilan integratsiya qilish uchun ishlatishimiz mumkin.
MutationObserver har qanday oâzgarishlarni 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â¦)