Qo'shimcha brauzer mavzulari

Mutation observer

MutationObserver — bu DOM daraxtini kuzatib turuvchi va unda o'zgarish ro'y berganda (element qo'shilganda yoki o'chirilganda, atribut o'zgarganda, matn tahrirlanganda) sizga xabar beruvchi maxsus obyekt. U ayniqsa sahifaga tashqaridan kelgan kodni (masalan, uchinchi tomon skriptlari yoki reklama bloklari) nazorat qilishda, redaktorlarni yozishda va kutubxonalarni integratsiya qilishda juda qadrli.

Nima uchun kerak

Tasavvur qiling, siz sahifaga uchinchi tomon skriptini qo'shdingiz — u foydali kontent bilan birga o'zining reklamasini yoki keraksiz <div>larini ham qo'shadi. Bu skriptning ichiga kira olmaysiz. Lekin uning DOMga qanday o'zgarish kiritishini kuzatib, keraksiz elementni darhol o'chirib tashlashingiz mumkin. Aynan shu vazifani MutationObserver bajaradi.

Yana bir keng tarqalgan holat — sintaksisni ranglaydigan kutubxonalar (Prism.js, highlight.js kabi). Yangi kod bloki sahifaga qo'shilganda uni avtomatik ravishda bo'yash kerak bo'ladi. Buni MutationObserver orqali eshitib turish mumkin.

Sintaksis

Avval kuzatuvchi yaratamiz, so'ng uni biror tugunga (node) bog'laymiz:

let observer = new MutationObserver(callback);

observer.observe(node, config);

callback — o'zgarish ro'y berganda chaqiriladigan funksiya. U ikkita argument oladi: o'zgarishlar ro'yxati (mutationRecords) va kuzatuvchining o'zi.

observe(node, config) — qaysi tugunni va aynan qanday o'zgarishlarni kuzatishni belgilaydi. config — bu mantiqiy (boolean) bayroqlar to'plami:

configda kamida childList, attributes yoki characterDatadan bittasi true bo'lishi shart. Aks holda observe xatolik beradi — chunki nima kuzatilishi noaniq bo'lib qoladi.

Oddiy misol

Quyida biror <div>ning ichidagi barcha o'zgarishlarni (butun poddaraxtni) kuzatamiz. E'tibor bering, bu kod brauzerda ishlaydi — bu yerda u statik namuna sifatida keltirilgan:

let elem = document.getElementById('kuzatiladigan');

let observer = new MutationObserver(mutationRecords => {
  console.log(mutationRecords); // o'zgarishlar ro'yxati
});

// har qanday o'zgarishni kuzatishni boshlaymiz
observer.observe(elem, {
  childList: true,        // to'g'ridan-to'g'ri bolalar
  subtree: true,          // barcha avlodlar ham
  characterData: true,    // matn o'zgarishlari
  attributes: true        // atributlar
});

// endi elem ichini o'zgartiramiz
elem.innerHTML = '<b>Salom</b> dunyo';

Kod ishlaganda konsolga MutationRecord obyektlaridan iborat massiv chiqadi.

MutationRecord obyekti

callbackga tushadigan har bir o'zgarish yozuvi (record) quyidagi asosiy xususiyatlarga ega:

Masalan, atribut o'zgarishini eski qiymati bilan qo'lga olamiz:

let observer = new MutationObserver(records => {
  for (let record of records) {
    if (record.type === 'attributes') {
      console.log(
        record.attributeName + ' atributi o'zgardi. ' +
        'Eski qiymat: ' + record.oldValue
      );
    }
  }
});

observer.observe(elem, {
  attributes: true,
  attributeOldValue: true
});
Bir callback chaqiruvida bir nechta o'zgarish yig'ilib kelishi mumkin. Shuning uchun records — bu massiv. Brauzer o'zgarishlarni to'plab, keyin bir yo'la yetkazadi (mikrovazifa sifatida — bu haqda uchinchi darsda gaplashamiz).

Qo'llanish: keraksiz elementni o'chirish

Sahifaga qo'shilayotgan reklama <div>larini avtomatik o'chiruvchi kuzatuvchi. childList va subtree yordamida yangi qo'shilgan tugunlarni ko'ramiz:

let observer = new MutationObserver(records => {
  for (let record of records) {
    for (let node of record.addedNodes) {
      // faqat elementlar bilan ishlaymiz (matn tugunlarini o'tkazamiz)
      if (!(node instanceof HTMLElement)) continue;

      if (node.classList.contains('reklama')) {
        node.remove();
        console.log('Reklama olib tashlandi');
      }
    }
  }
});

observer.observe(document.body, {
  childList: true,
  subtree: true
});

Qo'llanish: kutubxona integratsiyasi

Faraz qilaylik, bizda highlight() funksiyasi bor — u <pre> ichidagi kodni bo'yaydi. Yangi kod bloki sahifaga qanday yo'l bilan qo'shilishidan qat'i nazar (innerHTML, append yoki boshqa skript orqali), biz uni avtomatik bo'yashni istaymiz:

function highlight(block) {
  // ... kodni bo'yash mantig'i ...
  console.log('Bloklandi va bo'yaldi');
}

let observer = new MutationObserver(records => {
  for (let record of records) {
    for (let node of record.addedNodes) {
      if (!(node instanceof HTMLElement)) continue;

      // qo'shilgan tugunning o'zi <pre> bo'lsa
      if (node.matches('pre[class*="language-"]')) {
        highlight(node);
      }

      // yoki uning ichida <pre>lar bo'lsa
      for (let pre of node.querySelectorAll('pre[class*="language-"]')) {
        highlight(pre);
      }
    }
  }
});

observer.observe(document.body, { childList: true, subtree: true });
Bu usul juda kuchli: sahifaga kim va qanday kod qo'shishidan qat'i nazar, siz uni bir joyda "ushlab" qayta ishlaysiz. Redaktor, chat, dinamik ro'yxatlar — hammasi bilan ishlaydi.

observe konfiguratsiya kombinatsiyalari

Ba'zi keng tarqalgan sozlamalar:

disconnect va takeRecords

Kuzatishni to'xtatish uchun disconnect() chaqiriladi:

observer.disconnect();

takeRecords() — hali callbackga yetkazilmagan, ammo qayd etilgan o'zgarishlar ro'yxatini oladi va navbatni bo'shatadi. Bu odatda disconnect()dan oldin, "oxirgi paytida" yig'ilib qolgan o'zgarishlarni ham qo'lda qayta ishlash uchun ishlatiladi:

// hali qayta ishlanmagan o'zgarishlarni olamiz
let pending = observer.takeRecords();
process(pending);

// keyin kuzatishni to'xtatamiz
observer.disconnect();
disconnect() chaqirilgach, takeRecords() endi bo'sh massiv qaytaradi — chunki navbat allaqachon bo'shatilgan bo'ladi. Shu bois takeRecords()ni disconnect()dan oldin chaqiring.
MutationObserver kuzatuvchi obyekti kuzatilayotgan tugunlarga kuchsiz (weak) bog'lanmaydi — u tugunga qattiq ishora saqlaydi. Kuzatuvchini disconnect() qilmasangiz, u va u ushlab turgan tugunlar xotirada qolib ketishi mumkin (xotira sizib chiqishi). Kerak bo'lmaganda albatta uzib qo'ying.

Xotira boshqaruvi

Muhim jihat: kuzatuvchi kuzatayotgan tugunlar DOMdan olib tashlansa ham, ular xotirada saqlanib qolishi mumkin — chunki kuzatuvchi ularga ishora saqlaydi. Element endi kerak bo'lmasa, uni kuzatishdan to'xtatish yaxshi amaliyot. Odatda komponent "o'chirilganda" (unmount) disconnect() chaqiriladi.

Xulosa