14-bo‘lim

Hodisalar (events)

addEventListener, hodisa obyekti, ko'tarilish va tutish, delegatsiya, preventDefault va ishlovchini olib tashlash.

🕑 17 daqiqa o‘qish 📄 727 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Birinchi ishlovchi
  2. Hodisa obyekti
  3. Ko'tarilish (bubbling)
  4. Tutish bosqichi va to'xtatish
  5. Hodisa delegatsiyasi
  6. preventDefault
  7. Ishlovchini olib tashlash
  8. AbortController bilan tozalash
  9. Klaviatura hodisalari
  10. Maxsus hodisalar
  11. Xulosa

Foydalanuvchi tugmani bosdi, matn yozdi, sahifani aylantirdi - bularning har biri hodisa. Ilova shularga javob berib yashaydi.

JavaScript
document.body.innerHTML = `
<div id="ilova">
    <ul id="royxat">
        <li class="vazifa" data-id="1"><span>Non olish</span></li>
        <li class="vazifa" data-id="2"><span>Kitob o'qish</span></li>
    </ul>
    <button id="qoshish">Qo'shish</button>
    <a id="havola" href="https://example.com">Havola</a>
</div>`;

Birinchi ishlovchi #

JavaScript
const tugma = document.getElementById("qoshish");
let bosilgan = 0;

tugma.addEventListener("click", function () {
    bosilgan++;
});

// Bosishni dastur orqali taqlid qilamiz
tugma.click();
tugma.click();
tugma.click();

console.log("Necha marta bosildi:", bosilgan);
Natija
Necha marta bosildi: 3
addEventListener - nima uchun onclick emas

Uchta usul bor, lekin faqat bittasi to'g'ri:

JavaScript
// 1. HTML da - kod va tuzilma aralashadi
<button onclick="qilish()">Bosing</button>

// 2. Xossa orqali - faqat BITTA ishlovchi
tugma.onclick = birinchi;
tugma.onclick = ikkinchi;      // birinchisini o'chirdi!

// 3. addEventListener - bir nechta ishlovchi
tugma.addEventListener("click", birinchi);
tugma.addEventListener("click", ikkinchi);   // ikkalasi ham ishlaydi

addEventListener afzalliklari:

  • Bir nechta ishlovchi qo'shish mumkin;
  • Sozlamalar bor (once, capture, passive);
  • removeEventListener bilan olib tashlash mumkin;
  • HTML toza qoladi.

Hodisa obyekti #

JavaScript
const royxat = document.getElementById("royxat");
const natijalar = [];

royxat.addEventListener("click", function (hodisa) {
    natijalar.push(`type: ${hodisa.type}`);
    natijalar.push(`target: ${hodisa.target.tagName}`);
    natijalar.push(`currentTarget: ${hodisa.currentTarget.tagName}`);
    natijalar.push(`bubbles: ${hodisa.bubbles}`);
});

// Ichkaridagi span ni bosamiz
royxat.querySelector("span").click();

console.log(natijalar.join("\n"));
Natija
type: click
target: SPAN
currentTarget: UL
bubbles: true
target va currentTarget - eng ko'p chalkashtiriladigan juftlik
XossaNima
targetHodisa boshlangan element (foydalanuvchi tekkani)
currentTargetIshlovchi osilgan element

Yuqoridagi misolda:

  • Foydalanuvchi <span> ni bosdi → target = SPAN;
  • Ishlovchi <ul> da turibdi → currentTarget = UL.

Bu farq delegatsiyada hal qiluvchi ahamiyatga ega. Agar target ni ishlatsangiz, foydalanuvchi qayerga aniq tekkanini bilasiz - lekin bu ichkaridagi ixtiyoriy teg bo'lishi mumkin.

Shuning uchun deyarli har doim closest bilan birga ishlatiladi:

JavaScript
const vazifa = hodisa.target.closest(".vazifa");

Diqqat: arrow funksiyada this currentTarget emas. Oddiy function da esa this === currentTarget.

Ko'tarilish (bubbling) #

JavaScript
const tartib = [];

document.getElementById("ilova").addEventListener("click", () => {
    tartib.push("div (ilova)");
});

document.getElementById("royxat").addEventListener("click", () => {
    tartib.push("ul (royxat)");
});

document.querySelector(".vazifa").addEventListener("click", () => {
    tartib.push("li (vazifa)");
});

document.querySelector("span").addEventListener("click", () => {
    tartib.push("span");
});

document.querySelector("span").click();

console.log("Bajarilish tartibi:");
console.log(tartib.join(" -> "));
Natija
Bajarilish tartibi:
span -> li (vazifa) -> ul (royxat) -> div (ilova)
Hodisaning uch bosqichi document div ul li span 1. Tutish yuqoridan pastga capture: true 2. Nishon (target) 3. Ko'tarilish pastdan yuqoriga odatiy holat
Odatda faqat ko'tarilish bosqichi ishlatiladi

Tutish bosqichi va to'xtatish #

JavaScript
const tartib = [];

document.getElementById("royxat").addEventListener("click", () => {
    tartib.push("ul TUTISH");
}, { capture: true });

document.getElementById("royxat").addEventListener("click", () => {
    tartib.push("ul ko'tarilish");
});

document.querySelector("span").addEventListener("click", () => {
    tartib.push("span");
});

document.querySelector("span").click();
console.log(tartib.join(" -> "));

// stopPropagation - ko'tarilishni to'xtatadi
const tartib2 = [];
const yangiRoyxat = document.getElementById("royxat");

yangiRoyxat.addEventListener("click", () => tartib2.push("ul"));

const vazifa = document.querySelector(".vazifa");
vazifa.addEventListener("click", (h) => {
    tartib2.push("li");
    h.stopPropagation();
});

vazifa.click();
console.log("stopPropagation bilan:", tartib2.join(" -> "));
Natija
ul TUTISH -> span -> ul ko'tarilish
stopPropagation bilan: li
stopPropagation ni ehtiyotkorlik bilan ishlating

U hodisani to'xtatadi - yuqoridagi barcha ishlovchilar ishlamay qoladi.

Bu boshqa kodni sezdirmasdan buzadi:

JavaScript
// Modal oynani tashqariga bosilganda yopish
document.addEventListener("click", () => modalniYopish());

// Boshqa joyda kimdir yozgan:
tugma.addEventListener("click", (h) => h.stopPropagation());
// Endi modal yopilmaydi va sababi ko'rinmaydi

Ko'p hollarda stopPropagation o'rniga shartni tekshirish kifoya:

JavaScript
document.addEventListener("click", (h) => {
    if (h.target.closest(".modal")) {
        return;                    // modal ichida - tegmaymiz
    }
    modalniYopish();
});

Yana bir metod bor - stopImmediatePropagation. U shu elementdagi qolgan ishlovchilarni ham to'xtatadi. Undan foydalanish uchun juda kuchli sabab kerak.

Hodisa delegatsiyasi #

JavaScript
const royxat = document.getElementById("royxat");
const bosilganlar = [];

// BITTA ishlovchi - barcha element uchun
royxat.addEventListener("click", (hodisa) => {
    const vazifa = hodisa.target.closest(".vazifa");

    if (!vazifa) {
        return;                    // ro'yxatning bo'sh joyi bosildi
    }

    bosilganlar.push(vazifa.dataset.id);
});

royxat.querySelector("[data-id='1'] span").click();
royxat.querySelector("[data-id='2']").click();

// Yangi element qo'shamiz - ishlovchi kerak emas!
const yangi = document.createElement("li");
yangi.className = "vazifa";
yangi.dataset.id = "3";
yangi.textContent = "Yangi vazifa";
royxat.appendChild(yangi);

yangi.click();

console.log("Bosilgan id lar:", bosilganlar.join(", "));
console.log("Yangi element ham ishladimi?:", bosilganlar.includes("3"));
Natija
Bosilgan id lar: 1, 2, 3
Yangi element ham ishladimi?: true
Delegatsiya - eng muhim naqsh

Har bir elementga alohida ishlovchi osish o'rniga, bitta ishlovchini otaga osasiz va closest bilan kerakligini topasiz.

Afzalliklari:

MuammoDelegatsiya bilan
1000 element = 1000 ishlovchiBitta ishlovchi
Yangi element ishlamaydiAvtomatik ishlaydi
O'chirilgan element xotirada qoladiMuammo yo'q

Bu ayniqsa dinamik ro'yxatlarda muhim - siz element qo'shganda ishlovchi qo'shishni eslashingiz shart emas.

Naqshning to'liq shakli:

JavaScript
idish.addEventListener("click", (hodisa) => {
    const tugma = hodisa.target.closest("[data-amal]");
    if (!tugma || !idish.contains(tugma)) {
        return;
    }

    switch (tugma.dataset.amal) {
        case "ochirish": ochirish(tugma.dataset.id); break;
        case "tahrirlash": tahrirlash(tugma.dataset.id); break;
    }
});

Bitta ishlovchi butun ro'yxatni boshqaradi.

preventDefault #

JavaScript
const havola = document.getElementById("havola");
let ushlandi = false;

havola.addEventListener("click", (hodisa) => {
    hodisa.preventDefault();       // sahifaga o'tishni bekor qiladi
    ushlandi = true;
});

const hodisa = new MouseEvent("click", { bubbles: true, cancelable: true });
const natija = havola.dispatchEvent(hodisa);

console.log("Ishlovchi ishladimi?  :", ushlandi);
console.log("defaultPrevented      :", hodisa.defaultPrevented);
console.log("dispatchEvent natijasi:", natija);
console.log("Sahifa o'zgardimi?    : yo'q");
Natija
Ishlovchi ishladimi?  : true
defaultPrevented      : true
dispatchEvent natijasi: false
Sahifa o'zgardimi?    : yo'q
preventDefault va stopPropagation - butunlay boshqa narsa
MetodNimani to'xtatadi
preventDefaultBrauzerning odatiy harakati
stopPropagationHodisaning tarqalishi

Odatiy harakatlar:

HodisaOdatiy harakat
click havoladaYangi sahifaga o'tish
submit formadaSahifani qayta yuklash
contextmenuKontekst menyu ochish
keydown (Tab)Keyingi maydonga o'tish

Ikkalasi mustaqil: preventDefault chaqirsangiz ham hodisa yuqoriga ko'tarilaveradi.

Eski kodda return false uchraydi - u ikkalasini ham bajaradi, lekin faqat onclick xossasida. addEventListener da ishlamaydi. Aniq yozing.

Ishlovchini olib tashlash #

JavaScript
const tugma = document.getElementById("qoshish");
let hisob = 0;

function ishlovchi() {
    hisob++;
}

tugma.addEventListener("click", ishlovchi);
tugma.click();
tugma.click();
console.log("Olib tashlashdan oldin:", hisob);

tugma.removeEventListener("click", ishlovchi);
tugma.click();
console.log("Olib tashlagandan keyin:", hisob);

// once - bir marta ishlaydi va o'zi o'chadi
let birMarta = 0;
tugma.addEventListener("click", () => birMarta++, { once: true });

tugma.click();
tugma.click();
tugma.click();
console.log("once bilan:", birMarta);
Natija
Olib tashlashdan oldin: 2
Olib tashlagandan keyin: 2
once bilan: 1
Anonim funksiyani olib tashlab bo'lmaydi
JavaScript
tugma.addEventListener("click", () => console.log("salom"));
tugma.removeEventListener("click", () => console.log("salom"));  // ISHLAMAYDI

Ikki arrow funksiya bir xil ko'rinsa ham, boshqa obyekt. removeEventListener aynan o'sha havolani talab qiladi.

Uch yechim:

JavaScript
// 1. Nomlangan funksiya
function ishlovchi() { }
tugma.addEventListener("click", ishlovchi);
tugma.removeEventListener("click", ishlovchi);

// 2. once - o'zi o'chadi
tugma.addEventListener("click", () => {}, { once: true });

// 3. AbortController - bir nechtasini birdan
const boshqaruv = new AbortController();
tugma.addEventListener("click", () => {}, { signal: boshqaruv.signal });
oyna.addEventListener("resize", () => {}, { signal: boshqaruv.signal });

boshqaruv.abort();      // ikkalasi ham olib tashlanadi

Uchinchi usul komponent "o'lganda" hamma ishlovchini bir yo'la tozalash uchun ideal.

AbortController bilan tozalash #

JavaScript
const tugma = document.getElementById("qoshish");
const boshqaruv = new AbortController();

let birinchi = 0;
let ikkinchi = 0;

tugma.addEventListener("click", () => birinchi++, { signal: boshqaruv.signal });
tugma.addEventListener("click", () => ikkinchi++, { signal: boshqaruv.signal });

tugma.click();
console.log("abort dan oldin:", birinchi, ikkinchi);

boshqaruv.abort();
tugma.click();
tugma.click();

console.log("abort dan keyin:", birinchi, ikkinchi);
console.log("Ikkalasi ham to'xtadimi?:", birinchi === 1 && ikkinchi === 1);
Natija
abort dan oldin: 1 1
abort dan keyin: 1 1
Ikkalasi ham to'xtadimi?: true

Klaviatura hodisalari #

JavaScript
const maydon = document.createElement("input");
document.body.appendChild(maydon);

const bosilganlar = [];

maydon.addEventListener("keydown", (hodisa) => {
    bosilganlar.push(`${hodisa.key} (ctrl: ${hodisa.ctrlKey})`);

    if (hodisa.key === "Enter") {
        bosilganlar.push("  -> Enter bosildi");
    }
});

maydon.dispatchEvent(new KeyboardEvent("keydown", { key: "a" }));
maydon.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter" }));
maydon.dispatchEvent(new KeyboardEvent("keydown", { key: "s", ctrlKey: true }));

console.log(bosilganlar.join("\n"));
Natija
a (ctrl: false)
Enter (ctrl: false)
  -> Enter bosildi
s (ctrl: true)
key ishlating, keyCode emas

Eski kodda hodisa.keyCode === 13 uchraydi. keyCode standartdan chiqarilgan va turli klaviaturada farq qiladi.

key o'qiladigan nom beradi:

Tugmakey qiymati
Enter"Enter"
Escape"Escape"
Bo'shliq" "
O'q tugmalar"ArrowUp", "ArrowDown"
Harf"a", "A"

Modifikatorlar alohida xossalarda: ctrlKey, shiftKey, altKey, metaKey (Mac da Cmd).

Klaviatura qisqartmasi:

JavaScript
if (hodisa.ctrlKey && hodisa.key === "s") {
    hodisa.preventDefault();       // brauzerning "saqlash" oynasini bekor qiladi
    saqlash();
}

Mac da Cmd+S uchun metaKey ni ham tekshiring:

JavaScript
const boshqaruvTugmasi = hodisa.ctrlKey || hodisa.metaKey;

Maxsus hodisalar #

JavaScript
const idish = document.getElementById("ilova");
let qabulQilingan = null;

idish.addEventListener("vazifaQoshildi", (hodisa) => {
    qabulQilingan = hodisa.detail;
});

// O'z hodisamizni yaratamiz va yuboramiz
const hodisa = new CustomEvent("vazifaQoshildi", {
    detail: { id: 42, matn: "Yangi vazifa" },
    bubbles: true
});

document.querySelector(".vazifa").dispatchEvent(hodisa);

console.log("Qabul qilindi:", qabulQilingan !== null);
console.log("id           :", qabulQilingan.id);
console.log("matn         :", qabulQilingan.matn);
Natija
Qabul qilindi: true
id           : 42
matn         : Yangi vazifa
Amaliy topshiriq
  1. addEventListener bilan tugmaga ishlovchi osing.
  2. onclick va addEventListener farqini tushuntiring.
  3. target va currentTarget farqini misolda ko'rsating.
  4. To'rt darajali ko'tarilish tartibini kuzating.
  5. capture: true bilan tutish bosqichini sinang.
  6. stopPropagation ning zararli tomonini tushuntiring.
  7. Delegatsiya bilan bitta ishlovchida butun ro'yxatni boshqaring.
  8. Yangi qo'shilgan element ham ishlashini isbotlang.
  9. preventDefault bilan havolaga o'tishni bekor qiling.
  10. AbortController bilan ikki ishlovchini birdan olib tashlang.

Xulosa #

  • addEventListener ishlating - bir nechta ishlovchi va sozlamalar beradi.
  • target - tegilgan element, currentTarget - ishlovchi osilgan element.
  • Hodisa uch bosqichdan o'tadi: tutish → nishon → ko'tarilish.
  • stopPropagation boshqa kodni sezdirmasdan buzishi mumkin.
  • Delegatsiya - bitta ishlovchi butun ro'yxat uchun.
  • Delegatsiyada yangi element avtomatik ishlaydi.
  • preventDefault odatiy harakatni, stopPropagation tarqalishni to'xtatadi.
  • Anonim funksiyani removeEventListener bilan olib bo'lmaydi.
  • once: true ishlovchini bir martadan keyin o'chiradi.
  • AbortController bir nechta ishlovchini birdan tozalaydi.
  • Klaviaturada key ishlating, keyCode emas.
  • CustomEvent bilan o'z hodisalaringizni yarating.

Keyingi bo'limda formalar va validatsiyani ko'rib chiqamiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.