Hodisalarga kirish

Maxsus hodisalar

Nafaqat brauzer, biz ham o'z maxsus hodisalarimizni (custom events) yaratib, ularni istalgan elementda ishga tushira olamiz. Bu, ayniqsa, o'z komponentlaringni yozganda foydali: komponent "tashqi dunyoga" biror voqea yuz berganini hodisa orqali xabar qiladi.

Event konstruktori

Yangi hodisa obyekti Event ichki (built-in) konstruktori orqali yaratiladi:

let event = new Event(type, options);
Standart bo'yicha ikkovi ham false: { bubbles: false, cancelable: false }. Ya'ni maxsus hodisa aytmasangiz ko'tarilmaydi va bekor qilinmaydi.

dispatchEvent

Hodisa obyektini yaratgach, uni elementda "ishga tushirish" (fire) uchun elem.dispatchEvent(event) chaqiriladi. So'ng ishlovchilar go'yo bu haqiqiy brauzer hodisasidek javob beradi:

<button id="elem" onclick="alert('Klik!');">Avtoklik</button>

<script>
  let event = new Event('click');
  elem.dispatchEvent(event);
</script>

Bu yerda hech kim tugmani bosmasa ham, biz 'click' hodisasini dasturiy tarzda yaratib yubordik โ€” shuning uchun onclick ishlovchisi ishga tushadi va "Klik!" ko'rsatiladi.

event.isTrusted xossasi hodisa haqiqiy foydalanuvchi harakatidanmi (true) yoki skript orqali yaratilganmi (false) ekanini ajratadi. Skript orqali dispatchEvent qilingan hodisalarda u false bo'ladi.

Maxsus hodisalar ham ko'tariladi

O'z nomi bilan hodisa yaratib, uni ko'tariladigan qilishimiz mumkin. Buning uchun bubbles: true beramiz:

<h1 id="elem">Salom, dunyodan</h1>

<script>
  // hujjat darajasida maxsus 'hello' hodisasini tinglaymiz
  document.addEventListener('hello', function(event) {
    alert('Salom, ' + event.target.tagName); // Salom, H1
  });

  // ...va uni elementda ishga tushiramiz:
  let event = new Event('hello', { bubbles: true });
  elem.dispatchEvent(event);
</script>

Muhim jihatlar:

Yangi hodisa nomlarini o'ylab topsak ham, standart hodisa nomlaridan (masalan, 'click') foydalanmaslik yaxshi โ€” chalkashlik va kutilmagan xatti-harakatlar oldini olish uchun ko'pincha o'z prefiksli nomlar (masalan, 'menu-open') ishlatiladi.

CustomEvent va detail

Maxsus hodisaga qo'shimcha ma'lumot biriktirish uchun Event o'rniga maxsus CustomEvent konstruktori ishlatiladi. Uning detail degan qo'shimcha xossasi bor โ€” istalgan ma'lumotni shu orqali uzatamiz:

<h1 id="elem">Salom Ali uchun</h1>

<script>
  elem.addEventListener('hello', function(event) {
    alert(event.detail.name); // Ali
  });

  elem.dispatchEvent(new CustomEvent('hello', {
    detail: { name: 'Ali' }
  }));
</script>

detail xossasi istalgan turdagi qiymat bo'lishi mumkin: string, obyekt, massiv va hokazo. Ishlovchi ichida u event.detail orqali olinadi.

Texnik jihatdan CustomEvent deyarli Event bilan bir xil, bitta farqi bor: ikkinchi argumentda detail xossasini qabul qiladi. Nima uchun oddiy Eventga o'z xossalarini qo'shmaymiz? Chunki CustomEvent nomi kodni o'qiganlarga bu maxsus hodisa ekanini va unda qanaqadir qo'shimcha ma'lumot borligini aniq bildiradi โ€” bu yaxshi kelishuv (konvensiya).

Maxsus hodisalarni bekor qilish

Maxsus hodisalarda ham "standart harakat" tushunchasini yaratish mumkin. Buning uchun hodisani cancelable: true bilan yaratamiz, ishlovchi preventDefault() chaqirsa, dispatchEvent false qaytaradi:

<pre id="rabbit">Quyoncha</pre>
<button onclick="hide()">Yashir()</button>

<script>
  function hide() {
    let event = new CustomEvent('hide', {
      cancelable: true // preventDefault ishlashi uchun zarur
    });

    if (!rabbit.dispatchEvent(event)) {
      alert('Harakat ishlovchi tomonidan bekor qilindi');
    } else {
      rabbit.hidden = true;
    }
  }

  rabbit.addEventListener('hide', function(event) {
    if (confirm('preventDefault chaqiraymi?')) {
      event.preventDefault();
    }
  });
</script>

Bu yerda "hide" hodisasini tinglagan kod uni bekor qila oladi. dispatchEvent agar biror ishlovchi preventDefault() chaqirgan bo'lsa false, aks holda true qaytaradi โ€” shu orqali harakatni davom ettirish yoki to'xtatishni hal qilamiz.

preventDefault() faqat hodisa cancelable: true bilan yaratilgan bo'lsa ishlaydi. Aks holda uni chaqirish e'tiborsiz qoldiriladi.

Ichma-ich hodisalar sinxrondir

Odatda hodisalar navbatda (queue) qayta ishlanadi. Masalan, brauzer onclickni ishlab turgan payt yangi mousemove yuz bersa, uning ishlovchisi onclick tugagach chaqiriladi.

Ammo bir istisno bor: agar hodisa boshqa hodisa ichida dispatchEvent bilan ishga tushirilsa, u navbatni kutmaydi, balki darhol sinxron qayta ishlanadi. Ya'ni yangi hodisa ishlovchisi bajarilib bo'lgach, boshqaruv tashqi kodga qaytadi.

<button id="menu">Menyu (chertib ko'ring)</button>

<script>
  menu.onclick = function() {
    alert(1);

    // menu-open hodisasi darhol (sinxron) qayta ishlanadi
    menu.dispatchEvent(new CustomEvent('menu-open', { bubbles: true }));

    alert(2);
  };

  document.addEventListener('menu-open', () => alert('ichki'));
</script>

Chiqish tartibi: 1 → ichki → 2. Ya'ni dispatchEvent ichma-ich hodisani darhol, tashqi kod davom etishidan oldin ishlab bo'ladi.

Agar ichma-ich hodisa sinxron bo'lishini istamasangiz (masalan, cheksiz aylanish yoki chalkashlikni oldini olish uchun), uni dispatchEventdan keyin nol-kechikishli setTimeout(() => elem.dispatchEvent(event), 0) ichida chaqirish mumkin โ€” shunda u navbatga tushib, joriy kod tugagach ishlaydi.

O'z komponentlaringda maxsus hodisalar

Maxsus hodisalar mustaqil komponentlar yaratishda juda foydali. Komponent "tashqariga" o'z ichki holatidagi o'zgarishlarni maxsus hodisa orqali xabar qiladi, tashqi kod esa unga obuna bo'ladi. Bu komponentni tashqi koddan ajratib (decoupling) turadi โ€” komponent kim tinglayotganini bilmaydi.

// Komponent ichida (masalan, o'z galereyangiz):
function goNext(slider) {
  // ... slaydni almashtiramiz ...

  // tashqi dunyoga xabar beramiz:
  slider.elem.dispatchEvent(new CustomEvent('slide-changed', {
    bubbles: true,
    detail: { index: slider.current }
  }));
}

// Tashqi kod komponent ichini bilmasdan tinglaydi:
sliderElem.addEventListener('slide-changed', function(event) {
  console.log('Yangi slayd: ' + event.detail.index);
});
Bu naqsh brauzerning o'zi ishlatadigan usulga o'xshaydi: <input> input/change hodisalarini, <video> play/pause hodisalarini yuboradi. Siz ham o'z komponentlaringni shu uslubda yozib, uni "ekotizimga" tabiiy moslashtira olasiz.

Xulosa