DOM — Event Bubbling, Capturing, Delegation
Muammodan boshlaylik
Mahsulotlar ro'yxati bor — 100 ta element. Har biriga bosish hodisasi qo'shmoqchisiz:
const elementlar = document.querySelectorAll(".mahsulot");
elementlar.forEach(function(el) {
el.addEventListener("click", function() {
console.log(el.textContent);
});
});
Bu ishlaydi. Lekin 100 ta element — 100 ta tinglash funksiyasi xotirada saqlanadi.
Yangi mahsulot dinamik qo'shilsa — unga ham alohida handler qo'shish kerak:
const yangi = document.createElement("li");
yangi.className = "mahsulot";
yangi.textContent = "Yangi mahsulot";
ro_yxat.appendChild(yangi);
yangi.addEventListener("click", ...); // ← unutsangiz — ishlamaydi
Bu yondashuv noqulay va xotira isrofi. Yechim — Event Delegation. Uni tushunish uchun avval Event Bubbling kerak.
Event Bubbling — hodisa yuqoriga ko'tariladi
HTML elementlar bir-birining ichida joylashadi — daraxt tuzilmasi. Hodisa yuz berganda, u faqat o'sha elementda qolmaydi — ota elementlarga ko'tarilib chiqadi.
Hayotiy o'xshatma
Hovuzga tosh tashlansangiz — to'lqin markazdan tashqariga tarqaladi. Eng kichik doiradan boshlab, tobora kattaroq doiralar hosil bo'ladi. Event Bubbling — xuddi shunday. Hodisa boshlangan elementdan boshlab, ota elementlarga, undan ham yuqoriga — document gacha ko'tariladi.
<div id="tashqi">
<div id="ichki">
<button id="tugma">Bos</button>
</div>
</div>
document.getElementById("tugma").addEventListener("click", function() {
console.log("tugma");
});
document.getElementById("ichki").addEventListener("click", function() {
console.log("ichki div");
});
document.getElementById("tashqi").addEventListener("click", function() {
console.log("tashqi div");
});
Tugma bosilganda — uch handler ham ishlaydi:
tugma ← birinchi: hodisa yuz bergan joy
ichki div ← ikkinchi: ota element
tashqi div ← uchinchi: undan ham yuqori ota
Tugmani bosib, ota elementlardagi handlerlar ham ishlab ketdi. Bu — bubbling.
Event Delegation — bitta handler, hammasi uchun
Bubbling tufayli shunday qilish mumkin: ota elementga bitta handler qo'yiladi. Qaysi bola element bosilganini event.target orqali aniqlanadi.
Hayotiy o'xshatma
Katta mehmonxona kirishidagi bir qo'riqchi. Har bir xona eshigiga alohida qo'riqchi emas — biri kirishda turadi. Kim kirsa — tekshiradi: "Siz necha-nchi xona mehmonisiz?" Xona soni bo'yicha qaror qabul qiladi.
Event Delegation — o'sha bitta qo'riqchi. Ota element — kirish. event.target — kim kirdi.
const ro_yxat = document.querySelector("ul");
// 100 ta li uchun — bitta handler
ro_yxat.addEventListener("click", function(event) {
const bosilgan = event.target.closest("li"); // ← qaysi li bosildi?
if (!bosilgan) return; // ul ning o'zi bosilsa — e'tiborsiz
console.log(bosilgan.textContent);
bosilgan.classList.toggle("tanlangan");
});
Endi yangi element qo'shilsa — handler avtomatik ishlaydi. Yangi li ham ul ichida — bubbling tufayli ul ning handleri uni ham ko'radi.
event.target va event.currentTarget farqi
Bu ikki xususiyat bitta ko'rinadi — lekin har xil narsa ko'rsatadi.
event.target — hodisa haqiqatda yuz bergan element. Tugma bosilsa — tugma.
event.currentTarget — handler qo'shilgan element. Handler ul da bo'lsa — ul.
ro_yxat.addEventListener("click", function(event) {
console.log(event.target); // bosilgan li
console.log(event.currentTarget); // har doim ul — handler shu yerda
});
Delegation da event.target ishlatiladi — haqiqatda kim bosilganini bilish uchun.
closest — to'g'ri elementni topish
event.target doim kutilgan element bo'lmasligi mumkin. <li> ichida <span> bo'lsa, <span> bosilganda event.target — span, li emas.
<li class="mahsulot">
<span class="nomi">Noutbuk</span>
<span class="narx">8 000 000</span>
</li>
ro_yxat.addEventListener("click", function(event) {
// event.target = bosilgan span bo'lishi mumkin
const mahsulot = event.target.closest(".mahsulot"); // ← eng yaqin .mahsulot ota
if (!mahsulot) return;
console.log(mahsulot.querySelector(".nomi").textContent);
});
closest(selektor) — element va uning ota elementlarini ko'rib, selectorga mos eng yaqinini qaytaradi. Topilmasa — null.
event.stopPropagation — bubbling ni to'xtatish
Ba'zan hodisani yuqoriga chiqarmaslik kerak. Masalan: modal oyna ichidagi tugma bosildi — modal yopilmasligi kerak. Lekin modal tashqi qatlamida click hodisasi modal yopadi.
const modal = document.querySelector(".modal");
const modal_ichi = document.querySelector(".modal-ichi");
// Modal tashqariga bosilsa — yopilsin
modal.addEventListener("click", function() {
modal.classList.remove("ochiq");
});
// Modal ichiga bosilsa — yopilmasin
modal_ichi.addEventListener("click", function(event) {
event.stopPropagation(); // ← bubbling to'xtatildi, modal handler ishlamaydi
});
Nima uchun stopPropagation ehtiyotkorlik bilan ishlatiladi?
stopPropagation bubbling ni to'xtatadi — yuqoridagi handlerlar hech qachon ishlamaydi. Bu kutilmagan holatlarga olib kelishi mumkin: analytics, logging, boshqa komponentlar hodisani ko'ra olmaydi. Faqat zarur joyda ishlatiladi.
Capturing — pastga tushish fazasi
Hodisaning ikkita fazasi bor:
document
↓ capturing (pastga)
tashqi div
↓
ichki div
↓
tugma ← hodisa yuz berdi
↑ bubbling (yuqoriga)
ichki div
↑
tashqi div
↑
document
Odatda handlerlar bubbling fazasida ishlaydi. Capturing fazasida ishlatish uchun uchinchi argument true yoki { capture: true }:
el.addEventListener("click", handler, true); // capturing fazasida
el.addEventListener("click", handler, { capture: true }); // bir xil
Capturing — kamdan-kam kerak. Ko'pchilik holatlarda bubbling yetarli.
Tez-tez uchraydigan xatolar
Delegation da noto'g'ri element tekshirish:
// ❌ — faqat to'g'ridan li bosilsa ishlaydi, li ichidagi span bosilsa yo'q
ro_yxat.addEventListener("click", function(event) {
if (event.target.tagName === "LI") {
// span bosilsa — bu shart bajarilmaydi
}
});
// ✅ — closest har holda to'g'ri li ni topadi
ro_yxat.addEventListener("click", function(event) {
const li = event.target.closest("li");
if (!li) return;
// ...
});
stopPropagation ni haddan ko'p ishlatish:
// ❌ — har joyda stopPropagation — kutilmagan oqibatlar
el.addEventListener("click", function(event) {
event.stopPropagation();
// ...
});
// ✅ — faqat haqiqatan kerak bo'lgan joyda
Xulosa
Event Bubbling — hodisa yuz bergan elementdan boshlab, ota elementlarga ko'tarilib chiqadi.
Event Delegation — ota elementga bitta handler. event.target.closest() bilan qaysi bola bosilganini aniqlash. Ko'p element uchun ideal — xotira tejash, dinamik elementlar uchun ham ishlaydi.
event.target — hodisa yuz bergan element. event.currentTarget — handler qo'shilgan element.
event.stopPropagation() — bubbling ni to'xtatish. Ehtiyotkorlik bilan.
Keyingi dars
47-dars: DOM — forma bilan ishlash — input, submit, validation