Seriya: JavaScript Basics Dars 57

Event capturing

Event capturing bubbling'ning aksi ekani, addEventListener'ning uchinchi argumenti orqali capturing/bubbling tanlanishi, to'liq uch bosqichli oqim, capturing nima uchun kerakligi (stopPropagation'dan himoya, modal/dropdown), va brauzer bu ikkovini bitta yo'l massivi orqali qanday amalga oshirishi.

57-dars

Event capturing

Event capturing — bubbling'ning aksi: hodisa documentdan boshlab, DOM daraxti bo'ylab yuqoridan pastga, aynan bosilgan elementga (target) qarab "tushib boradi". Bu — oldingi mavzuda ko'rgan capturing phase — u har doim sodir bo'ladi, lekin odatda ko'rinmaydi, chunki addEventListener standart holatda faqat bubbling'ni "tinglaydi".

<div id="outer">
  <div id="inner">
    <button id="btn">Bos</button>
  </div>
</div>
document.getElementById('outer').addEventListener('click', () => console.log('outer capturing'), true);
document.getElementById('inner').addEventListener('click', () => console.log('inner capturing'), true);
document.getElementById('btn').addEventListener('click', () => console.log('btn capturing'), true);

addEventListenerning uchinchi argumenti — true (yoki { capture: true }) — aynan shu: "Bu listener'ni bubbling paytida emas, capturing paytida ishga tushir" degan buyruq.

Natija:

outer capturing
inner capturing
btn capturing

Diqqat qiling — tartib teskari: eng tashqi elementdan boshlanib, eng ichkariga qarab boradi. Bubbling'da esa aksincha edi (btn → inner → outer).


1. To'liq uch bosqichni birga ko'rish

Agar bir elementga ham capturing, ham bubbling listener qo'ysak:

['outer', 'inner', 'btn'].forEach(id => {
  document.getElementById(id).addEventListener('click', () => console.log(id + ' CAPTURE'), true);
  document.getElementById(id).addEventListener('click', () => console.log(id + ' BUBBLE'), false);
});

btnni bossangiz, natija aniq shu tartibda chiqadi:

outer CAPTURE
inner CAPTURE
btn CAPTURE
btn BUBBLE
inner BUBBLE
outer BUBBLE

Bu — Event bubbling mavzusida ko'rgan uch bosqichning to'liq, real manzarasi:

document → html → body → outer → inner → btn   (CAPTURING — yuqoridan pastga)
                                    btn          (TARGET — capture va bubble ikkovi ham shu yerda)
btn → inner → outer → body → html → document   (BUBBLING — pastdan yuqoriga)

Har bir hodisa — doim shu uch bosqichdan o'tadi, farqi shundaki, siz addEventListenerning uchinchi argumenti orqali qaysi bosqichda "tinglashni" tanlaysiz.


2. Nega bu ikki bosqich umuman kerak — capturing nima uchun mavjud?

Oldingi mavzuda aytilganidek — bu tarixiy kelib chiqish (Netscape vs Microsoft), lekin amaliy foydasi ham bor: capturing sizga "ichkaridagi elementning o'ziga yetib borishidan oldin, uni ushlab qolish" imkonini beradi.

Eng aniq misol — modal oyna yoki dropdown'ni yopish:

document.addEventListener('click', (event) => {
  if (!dropdown.contains(event.target)) {
    dropdown.style.display = 'none';
  }
}, true); // capturing — sahifadagi HAR QANDAY bosishni birinchi bo'lib "ko'radi"

Bu yerda capturing ishlatilishining sababi: agar ichkaridagi biror tugma o'zining click listener'ida stopPropagation() chaqirsa (bubbling'ni to'xtatsa), oddiy bubbling listener hech qachon ishga tushmaydi. Lekin capturing listener — bubbling boshlanishidan oldin, yo'lning eng boshida allaqachon ishlagan bo'ladi, shuning uchun u stopPropagation()dan himoyalangan.


3. Brauzer darajasida

V8/Blink darajasida, capturing va bubbling — bitta yagona algoritmning ikki yarmi, alohida mexanizmlar emas. Hodisa yuz berganda, Blink:

  1. targetdan documentgacha bo'lgan to'liq yo'lni (path) DOM daraxti bo'ylab avvaldan hisoblab, massiv sifatida tuzadi: [document, html, body, outer, inner, btn]
  2. Shu massivni boshidan oxirigacha yurib chiqadi (capturing) — har bir node'da, agar capture: true bilan ro'yxatga olingan listener bo'lsa, chaqiradi
  3. targetga yetgach — o'sha yerdagi barcha listener'larni (capture ham, bubble ham, qo'shilgan tartibda) chaqiradi
  4. Keyin massivni teskari tartibda (oxiridan boshigacha) yurib chiqadi (bubbling) — har bir node'da capture: false (standart) listener'larni chaqiradi

Ya'ni bu — oldingi mavzularda ko'rgan oddiy massiv bo'ylab ikki marta (oldinga va orqaga) yurish kabi sodda tuzilma, faqat brauzer C++ darajasida buni juda tez bajaradi. Hech qanday "sehr" yo'q — bitta yo'l massivi, ikki yo'nalishda aylanish, har bir bekatda tegishli listener ro'yxatini tekshirish.


4. stopPropagation() — capturing bosqichida ham ishlaydi

document.getElementById('inner').addEventListener('click', (event) => {
  event.stopPropagation();
  console.log("inner CAPTURE — to'xtatildi");
}, true);

Agar stopPropagation() capturing paytida chaqirilsa — endi hodisa innerdan pastga (ya'ni btnga) hech qachon yetib bormaydi, target phase ham, bubbling phase ham umuman ishga tushmaydi. Bu — capturing'ning yana bir kuchli tomoni: pastdagi hech narsa hodisani ko'ra olmaydi, chunki yo'l boshidayoq to'xtatilgan.


5. Qachon capturing, qachon bubbling ishlatiladi — amaliy qoida

Holat Qaysi bosqich
Oddiy tugma, forma, click handler Bubbling (standart, 99% holat)
Event delegation (bitta ota-elementga umumiy listener) Bubbling
Modal/dropdown tashqarisiga bosishni aniqlash Capturing
Ichkaridagi kodning stopPropagation()idan "himoyalanish" kerak bo'lganda Capturing
Sahifada global "log qilish" yoki analytics (har bir bosishni, hech narsa to'xtatmasdan, birinchi bo'lib ushlash) Capturing

Amaliyotda, dasturchilarning ko'pchiligi deyarli har doim bubblingdan foydalanadi — capturing kamdan-kam, faqat yuqoridagi kabi maxsus holatlarda kerak bo'ladi.


Xulosa

Event capturing — hodisaning documentdan targetgacha yuqoridan pastga "tushish" bosqichi, bubbling bilan bir xil yo'l massividan, lekin teskari yo'nalishda foydalanadi, va u sizga ichkaridagi elementga yetib borishidan oldin hodisani ushlab qolish imkonini beradi.