# JavaScript hodisa delegatsiyasi va ko'pikchi

> Hodisa ko'pikchisi (bubbling), delegatsiya naqshi, event.target va closest, stopPropagation hamda dinamik elementlarni boshqarish. Amaliy misollar bilan.

Manba: https://learncode.uz/javascript/hodisa-delegatsiya/
Sana: 2026-06-05

Yuzta tugmaga yuzta tinglovchi qo'yish shart emas. [Hodisalar](https://learncode.uz/javascript/hodisalar)
sahifa bo'ylab yuqoriga ko'tarilishi tufayli bittasi bilan cheklanish mumkin.

## Ko'pikchi — hodisa yuqoriga ko'tariladi

```js
document.querySelector("#outer").addEventListener("click", () => {
  console.log("3 — outer div");
});

document.querySelector("#average").addEventListener("click", () => {
  console.log("2 — o'rtacha div");
});

document.querySelector("#inner").addEventListener("click", () => {
  console.log("1 — button");
});

document.querySelector("#inner").click();
```

```html
<div id="outer" style="padding:20px;background:#eee">
  outer
  <div id="average" style="padding:20px;background:#ddd">
    o'rtacha
    <button id="inner">Bosing</button>
  </div>
</div>
```

Bitta bosishdan **uchta** xabar chiqdi. Hodisa tugmadan boshlanib, ota
elementlar bo'ylab yuqoriga ko'tarildi.

> **Atama**
>
> **Ko'pikchi** (bubbling) — hodisaning nishon elementdan boshlab ota elementlar
> bo'ylab yuqoriga, `document` gacha ko'tarilishi. Suv ostidagi havo pufagi
> yuzaga ko'tarilgani kabi.

## target va currentTarget

```js
document.querySelector("#box").addEventListener("click", (e) => {
  console.log("target:", e.target.tagName, e.target.id);
  console.log("currentTarget:", e.currentTarget.id);
  console.log("—");
});

document.querySelector("#a").click();
document.querySelector("#b").click();
```

```html
<div id="box" style="padding:20px;background:#eee">
  <button id="a">A</button>
  <button id="b">B</button>
</div>
```

Farq muhim:

- `target` — hodisa **aslida qayerda** yuz berdi (bosilgan tugma);
- `currentTarget` — tinglovchi **qayerga** o'rnatilgan (quti).

## Delegatsiya naqshi

Shu farqdan foydalanib, bitta tinglovchi bilan ko'p elementni boshqarish:

```js
const ul = document.querySelector("#list");

ul.addEventListener("click", (e) => {
  if (e.target.dataset.action !== "remove") return;

  const li = e.target.closest("li");
  console.log("O'chirildi:", li.textContent.trim());
  li.remove();
});

ul.querySelectorAll("button")[1].click();
console.log("Qolgan:", ul.children.length);
```

```html
<ul id="list">
  <li>Birinchi <button data-action="remove">×</button></li>
  <li>Ikkinchi <button data-action="remove">×</button></li>
  <li>Uchinchi <button data-action="remove">×</button></li>
</ul>
```

> **Atama**
>
> **Delegatsiya** — tinglovchini har bir elementga emas, ularning **umumiy ota
> elementiga** o'rnatib, `event.target` orqali qaysi biri bosilganini aniqlash.

## Nega delegatsiya kerak

Asosiy sabab — **keyin qo'shilgan** elementlar:

```js
const plain = document.querySelector("#plain");
const delegated = document.querySelector("#delegated");

// 1-usul: har elementga alohida tinglovchi
function addPlain(text) {
  const li = document.createElement("li");
  li.textContent = "plain: " + text;
  li.addEventListener("click", () => console.log("plain onClick"));
  plain.append(li);
}

// 2-usul: delegatsiya — tinglovchi bir marta, otaga
delegated.addEventListener("click", (e) => {
  if (e.target.tagName === "LI") console.log("delegated onClick");
});

function addDelegated(text) {
  const li = document.createElement("li");
  li.textContent = "delegated: " + text;
  delegated.append(li);
}

addPlain("A");
addDelegated("A");

plain.firstChild.click();
delegated.firstChild.click();

console.log("Ikkalasi ishladi — lekin delegatsiyada tinglovchi bitta");
```

```html
<ul id="plain"></ul>
<ul id="delegated"></ul>
```

Birinchi usulda har yangi element uchun tinglovchi qo'shish **esdan
chiqmasligi** kerak. Delegatsiyada esa bir marta yozilgan tinglovchi
kelajakdagi elementlarga ham ishlaydi.

## closest — kerakli otani topish

```js
const menu = document.querySelector("#menu");

menu.addEventListener("click", (e) => {
  const li = e.target.closest("li");
  if (!li) return;

  console.log("Bosilgan el:", e.target.tagName);
  console.log("Topilgan li, id:", li.dataset.id);
});

menu.querySelector("b").click();
```

```html
<ul id="menu">
  <li data-id="1"><span><b>Birinchi</b></span></li>
  <li data-id="2"><span><b>Ikkinchi</b></span></li>
</ul>
```

`<b>` bosildi, lekin `closest("li")` yuqoriga qarab kerakli otani topdi.
Ichma-ich elementlar bo'lganda bu majburiy — `target` har doim eng ichkarisi
bo'ladi.

## stopPropagation — ko'tarilishni to'xtatish

```js
document.querySelector("#outer").addEventListener("click", () => {
  console.log("outer — bu chiqmaydi");
});

document.querySelector("#inner").addEventListener("click", (e) => {
  e.stopPropagation();
  console.log("faqat button");
});

document.querySelector("#inner").click();
```

```html
<div id="outer" style="padding:20px;background:#eee">
  <button id="inner">Bosing</button>
</div>
```

> **Ehtiyot bo'ling**
>
> `stopPropagation` boshqa kodning tinglovchilarini ham to'sadi — masalan
> "tashqariga bosilganda menyuni yopish" mantig'ini buzadi. Uni faqat zarur
> bo'lganda ishlating; ko'pincha `target` ni tekshirish yetarli.

## Amaliy misol — vazifalar ro'yxati

```js
const ul = document.querySelector("#list");
const input = document.querySelector("#text");

document.querySelector("#add").addEventListener("click", () => {
  const li = document.createElement("li");

  const span = document.createElement("span");
  span.textContent = input.value;

  const doneBtn = document.createElement("button");
  doneBtn.textContent = "✓";
  doneBtn.dataset.action = "done";

  const removeBtn = document.createElement("button");
  removeBtn.textContent = "×";
  removeBtn.dataset.action = "remove";

  li.append(span, doneBtn, removeBtn);
  ul.append(li);
});

// Bitta tinglovchi — barcha tugmalar uchun
ul.addEventListener("click", (e) => {
  const action = e.target.dataset.action;
  if (!action) return;

  const li = e.target.closest("li");
  const span = li.querySelector("span");

  if (action === "done") {
    span.style.textDecoration = "line-through";
    console.log("Bajarildi:", span.textContent);
  } else if (action === "remove") {
    console.log("O'chirildi:", span.textContent);
    li.remove();
  }
});

document.querySelector("#add").click();
document.querySelector("#add").click();
console.log("Qo'shildi:", ul.children.length, "ta");
```

```html
<input id="text" value="Yangi vazifa">
<button id="add">Qo'shish</button>
<ul id="list"></ul>
```

`data-amal` atributi qaysi tugma bosilganini aytadi — bu delegatsiyadagi
standart usul.

## Amaliy mashq

1. Ichma-ich uch div yasab, ko'pikchi tartibini kuzating;
2. `target` va `currentTarget` ni bir tinglovchida chiqaring;
3. ro'yxatga delegatsiya bilan o'chirish tugmasini qo'shing;
4. yangi element qo'shib, tinglovchisiz ham ishlashini tasdiqlang;
5. `closest` bilan ichki `<b>` dan `<li>` ni toping;
6. `stopPropagation` qo'shib, tashqi tinglovchi to'xtaganini ko'ring.

## Qisqacha xulosa

- Hodisa nishondan yuqoriga **ko'tariladi** (bubbling).
- `target` — hodisa yuz bergan element, `currentTarget` — tinglovchi joyi.
- **Delegatsiya**: tinglovchi otaga, tekshiruv `target` orqali.
- Bu keyin qo'shilgan elementlarga ham avtomatik ishlaydi.
- `closest(selektor)` yuqoriga qarab mos otani topadi.
- `stopPropagation` ko'tarilishni to'xtatadi — ehtiyot bilan ishlating.

Keyingi darsda [DOM bilan chuqurroq ishlash](https://learncode.uz/javascript/dom-yaratish) —
element yaratish va joylashtirish.
