UI hodisalari

mouseover/out, mouseenter/leave

Kursor elementga kirib-chiqishini kuzatuvchi hodisalarni ko'rib chiqamiz. Ular ikki juftlikda keladi: mouseover/mouseout va mouseenter/mouseleave. Farqlarini tushunmaslik ko'p xatoliklarga sabab bo'ladi.

Bu darsdagi barcha misollar statik โ€” DOM hodisalari playground'da ishlamaydi. Ularni HTML faylingizda sinang.

mouseover va mouseout hodisalari

mouseover hodisasi kursor element ustiga kelganda, mouseout esa undan chiqib ketganda yuzaga keladi.

elem.onmouseover = function(event) {
  console.log('Kursor keldi, kelib chiqqan element: ' + event.target.tagName);
};

elem.onmouseout = function(event) {
  console.log('Kursor ketdi');
};

Bu hodisalar maxsus relatedTarget xossasiga ega. U targetni to'ldiradi:

relatedTarget qiymati null bo'lishi mumkin. Bu normal holat: masalan, kursor oyna tashqarisidan kelgan yoki oyna tashqarisiga chiqib ketgan bo'lsa, kelib chiqqan/borgan element bo'lmaydi. Kodingizda relatedTarget bilan ishlaganda uni tekshirishni unutmang, aks holda Cannot read property 'tagName' of null kabi xatolik olishingiz mumkin.

Harakatlarni o'tkazib yuborish (skip)

mousemove hodisasi kursor har harakatlanganda ishga tushadi. Lekin bu har bir pikselni kuzatishni anglatmaydi โ€” brauzer kursor pozitsiyasini vaqti-vaqti bilan tekshiradi. Shuning uchun kursor tez harakat qilsa, ba'zi oraliq DOM elementlari o'tkazib yuborilishi mumkin.

Masalan, kursor sahifa markazidan boshqa tomonga tez o'tsa, orada joylashgan elementlar (yoki hatto butun oyna) e'tiborga olinmasligi mumkin. Bunday holatda mouseover to'g'ridan-to'g'ri chetdagi elementga tegishli bo'ladi.

Bu qulay xususiyat: agar bir vaqtning o'zida ko'p elementlarni qayta ishlashga to'g'ri kelmasa, brauzer resurslarni tejaydi. Lekin natijada ba'zi mouseover/mouseout juftliklari "yo'qolib qolishi" mumkin. Agar biror element uchun mouseover ishga tushgan bo'lsa, uning uchun mos mouseout ham kafolatlanadi.

"Bolalar" elementlarga o'tishdagi muammo

Muhim va ko'pincha kutilmagan xatti-harakat: mouseover element ustiga kelganda ishga tushadi, biroq kursor bola (child) elementga o'tsa ham mouseout ota-elementda ishga tushib qoladi!

Brauzer nuqtai nazaridan mantiq shunday: kursor bir vaqtning o'zida faqat bitta eng chuqur (topmost) element ustida bo'la oladi โ€” bu eng ichkaridagi va eng ustunlik (z-index) bo'yicha tepadagi element. Shuning uchun kursor ota-elementdan uning ichidagi bola elementga kirsa, texnik jihatdan u ota-elementni tark etib, bolaga kirgan hisoblanadi.

// #parent ichida #child bor deylik.
// Kursor #parent'dan #child'ga o'tsa, bu hodisalar ishga tushadi:
// 1) #parent uchun mouseout (target=#parent, relatedTarget=#child)
// 2) #child uchun mouseover (target=#child, relatedTarget=#parent)

Bu shuni anglatadiki, agar onmouseout ishlov beruvchisida elementni yashirsak yoki uni "kursor ketdi" deb hisoblasak, kursor aslida hali ota-element ichida (uning bolasida) bo'lsa ham, yolg'on "ketish" hodisasini olamiz. Bu miltillash (flicker) va noto'g'ri xatti-harakatlarga olib keladi.

Agar shunchaki "kursor elementga (bolalari bilan birga) kirdi/chiqdi" faktini kuzatmoqchi bo'lsangiz, quyidagi mouseenter/mouseleavedan foydalaning โ€” ular aynan shu muammoni hal qiladi.

mouseenter va mouseleave hodisalari

mouseenter va mouseleave hodisalari mouseover/mouseoutga o'xshaydi: ular ham kursor kirib-chiqishida ishga tushadi. Biroq ikki muhim farq bor:

  1. Element ichidagi o'tishlar (masalan, ota-elementdan bolasiga o'tish) hisobga olinmaydi. mouseenter faqat kursor butun elementga (bolalari bilan birga) birinchi marta kirganda ishga tushadi; mouseleave esa kursor uni butunlay tark etganda.
  2. Bu hodisalar ko'tarilmaydi (bubbling qilmaydi).

Bu ikki farq ishlashni ancha soddalashtiradi. Kursor element ichidagi bolalar ustidan qanchalik o'tmasin, mouseenter faqat bir marta kirishda, mouseleave esa bir marta to'liq chiqishda ishga tushadi:

elem.onmouseenter = function(event) {
  // Faqat kursor 'elem' (va uning bolalari) ichiga kirganda.
  // relatedTarget bu yerda ham mavjud.
};

elem.onmouseleave = function(event) {
  // Faqat kursor 'elem'ni butunlay tark etganda.
};
mouseenter/mouseleave bubbling qilmagani uchun, ularni hodisa delegatsiyasi (event delegation) bilan ishlata olmaysiz. Ya'ni bitta ota-elementga ishlov beruvchi qo'yib, uning ichidagi ko'plab bolalarni bittada kuzatib bo'lmaydi. Har bir kerakli elementga alohida ishlov beruvchi qo'yish kerak. Bunday delegatsiya zarur bo'lsa, mouseover/mouseout va relatedTarget tekshiruvi bilan ishlashga to'g'ri keladi.

Qaysi birini tanlash kerak?

Amaliy tavsiya:

Xulosa