Seriya: JavaScript Basics Dars 56

Event bubbling

Event bubbling nima ekani, capturing/target/bubbling uch bosqichli model qanday paydo bo'lgani, brauzer darajasida bubbling zanjiri qanday ishlashi, event.target vs event.currentTarget, stopPropagation(), va qaysi hodisalar bubbling qilmasligi.

56-dars

Event bubbling

Foydalanuvchi biror elementni bossa (yoki boshqa hodisa yuz bersa), brauzer bu hodisani faqat o'sha elementda emas, balki uning barcha ota-elementlarida ham, ketma-ket, pastdan yuqoriga qarab ishga tushiradi. Bu jarayon bubbling (ko'pirish) deyiladi — xuddi suvdagi havo pufakchasi tubdan yuzaga ko'tarilgani kabi.

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

Agar foydalanuvchi btnni bossa, konsolda uchtasi ham chiqadi, va aniq shu tartibda:

btn
inner
outer

Garchi siz faqat btnni bossangiz ham — hodisa avval eng ichkarida ishga tushadi, keyin ota-elementlarga birma-bir "ko'tariladi", xuddi DOM daraxtidagi yo'lni orqaga (pastdan yuqoriga) bosib o'tganday.


1. Nega bunday qilingan — nima uchun faqat btnda ishlab, to'xtab qolmaydi?

Bu — tarixiy va amaliy sabab. 1990-larda ikkita brauzer kompaniyasi (Netscape va Microsoft) event modelini ikki xil yo'nalishda loyihalagan edi:

  • Netscape — hodisa yuqoridan pastga (capturing) borishi kerak, deb hisobladi
  • Microsoft — hodisa pastdan yuqoriga (bubbling) borishi kerak, deb hisobladi

W3C standartlashtirish paytida (DOM Level 2 Events, 2000) ikkovini ham birlashtirdi: hodisa avval yuqoridan pastga tushadi (capturing phase), eng pastki nishonga yetadi (target phase), keyin yana pastdan yuqoriga ko'tariladi (bubbling phase). Ya'ni bugungi standart — ikkala tarixiy yondashuvning kombinatsiyasi.

Amaliy foyda esa katta: agar bubbling bo'lmasa, sizga har bir tugma uchun alohida-alohida addEventListener yozish kerak bo'lardi. Bubbling tufayli — bitta ota-elementga bitta listener qo'yib, ichidagi barcha bolalar uchun ishlaydigan mexanizm yaratish mumkin (buni keyingi mavzu — Event delegationda chuqur ko'ramiz).


2. Brauzer darajasida — bu qanday ishlaydi

Foydalanuvchi sichqonchani bosganda, quyidagi zanjir ishga tushadi:

1. Hodisa yaratiladi

Operatsion tizim brauzerga "sichqoncha bosildi, X,Y koordinata shu yerda" degan xom signal yuboradi. Blink engine bu koordinata orqali, Render tree'dan qaysi element o'sha nuqtada joylashgan ekanini aniqlaydi (hit testing deyiladi) — bu, masalan, btn bo'ladi.

2. Event obyekti yaratiladi

Brauzer MouseEvent obyektini yaratadi, unda target maydoniga aynan bosilgan element (btn) yoziladi. Bu — hech qachon o'zgarmaydi, bubbling davomida ham event.target doim btn bo'lib qoladi.

3. Capturing phase (yuqoridan pastga)

Brauzer documentdan boshlab, html → body → outer → inner → btn yo'nalishida, agar biror elementda capturing listener (addEventListener('click', fn, true) — uchinchi argument true) bo'lsa, ularni shu yo'nalishda ishga tushiradi. (Amalda capturing kam ishlatiladi, lekin u — birinchi bosqich, har doim sodir bo'ladi.)

4. Target phase

Hodisa aynan btnga yetadi, shu yerdagi listener'lar (capturing ham, bubbling ham) ishga tushadi.

5. Bubbling phase (pastdan yuqoriga)

Endi brauzer orqaga qaytadi: btn → inner → outer → body → html → document, va har bir bosqichda, agar o'sha elementda bubbling listener (addEventListener('click', fn) — standart, uchinchi argument yo'q yoki false) bo'lsa, uni navbat bilan chaqiradi.

Bu jarayon — V8 darajasida oddiygina, ketma-ket funksiya chaqiruvlari zanjiri: brauzer ichki C++ kodida DOM daraxti bo'ylab yuradi, har bir node uchun "bu yerda shu turdagi listener bormi?" deb tekshiradi, bo'lsa — ro'yxatdagi callback funksiyalarni Call Stackga navbat bilan qo'yib chaqiradi. Bu — asinxron emas, butunlay sinxron jarayon: bitta click hodisasining bubbling zanjiri to'liq tugagunicha, boshqa hech narsa (masalan, boshqa click) orada kirmaydi.


3. event.target vs event.currentTarget

Bu — bubbling'ni tushunishda eng ko'p adashtiradigan joy:

document.getElementById('outer').addEventListener('click', (event) => {
  console.log('target:', event.target.id);         // btn (doim bosilgan element)
  console.log('currentTarget:', event.currentTarget.id); // outer (listener qo'yilgan element)
});
  • event.target — foydalanuvchi aynan qayerni bosgani (o'zgarmas, hodisa umri davomida bir xil)
  • event.currentTarget — hozir qaysi listener ishga tushayotgani (bubbling davomida har safar o'zgaradi: btn, keyin inner, keyin outer)

4. Bubbling'ni to'xtatish — stopPropagation()

document.getElementById('inner').addEventListener('click', (event) => {
  event.stopPropagation(); // bubbling shu yerda to'xtaydi
  console.log('inner');
});

Agar shuni qo'shsak, avvalgi misolda natija endi shunday bo'ladi:

btn
inner
// outer — chiqmaydi, chunki zanjir "inner"da uzildi

stopPropagation() — brauzerning ichki bubbling algoritmiga: "orqaga ko'tarilishni shu yerda to'xtat" deb buyruq beradi. Bu — juda ehtiyotkorlik bilan ishlatilishi kerak, chunki ko'pincha boshqa kod (masalan, tashqi kutubxona) ota-elementga listener qo'ygan bo'lishi mumkin, va sizning stopPropagation()ingiz kutilmaganda o'sha kodni butunlay ishlamay qo'yadi — bu debug qilish qiyin bo'lgan xatolarning keng tarqalgan manbai.

Farqlash kerak bo'lgan yana bir metod — stopImmediatePropagation(): bu nafaqat yuqoriga ko'tarilishni to'xtatadi, balki xuddi shu elementdagi qolgan boshqa listener'larni ham ishga tushirmaydi (agar bitta elementga bir nechta click listener qo'shilgan bo'lsa).


5. Har bir hodisa ham bubbling qilavermaydi

Muhim nuance: barcha event turlari bubbling qilmaydi. Masalan:

element.addEventListener('focus', handler);  // bubbling QILMAYDI
element.addEventListener('focusin', handler); // bubbling QILADI (focus'ning bubbling versiyasi)

focus, blur, mouseenter, mouseleave — bular maxsus, bubbling qilmaydigan hodisalar sifatida spec'da belgilangan (odatda, chunki ularning "ko'tarilishi" mantiqan foydali emas — masalan, sichqoncha elementga kirganda, uning ota-elementiga kirgani haqida alohida signal keraksiz bo'lardi, shuning uchun mouseover/mouseout ishlatiladi, ular bubbling qiladi). Qaysi hodisa bubbling qilishini event.bubbles xususiyati orqali tekshirish mumkin.


Xulosa

Event bubbling — DOM daraxtida yuz bergan hodisaning, eng ichki nishondan (target) boshlab, ota-elementlar bo'ylab yuqoriga, ketma-ket, sinxron tarzda "ko'tarilishi", va bu — brauzer tomonidan capturing→target→bubbling uch bosqichli standart model asosida amalga oshiriladi.