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.
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:
mouseoveruchun:event.targetโ kursor kelgan element;event.relatedTargetโ kursor qayerdan kelgani (avvalgi element).mouseoutuchun:event.targetโ kursor tark etgan element;event.relatedTargetโ kursor qayerga ketgani (yangi element).
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.
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.
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:
- Element ichidagi o'tishlar (masalan, ota-elementdan bolasiga o'tish) hisobga olinmaydi.
mouseenterfaqat kursor butun elementga (bolalari bilan birga) birinchi marta kirganda ishga tushadi;mouseleaveesa kursor uni butunlay tark etganda. - 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:
- Agar shunchaki "kursor bu elementga kirdimi/chiqdimi" degan oddiy holatni kuzatmoqchi bo'lsangiz โ
mouseenter/mouseleaveishlating. Ular ishonchli va soddaroq. - Agar hodisa delegatsiyasi kerak bo'lsa (masalan, katta jadval kataklarini bittada kuzatish) โ
mouseover/mouseoutvarelatedTargettekshiruvidan foydalaning.
Xulosa
mouseover/mouseoutโ kursor element yoki uning bolalari ustidan o'tganda ham ishga tushadi, bubbling qiladi,relatedTargetberadi. Bolalar bilan muammoga duch keladi.mouseenter/mouseleaveโ faqat elementga to'liq kirish/chiqishda ishga tushadi, ichki o'tishlarni e'tiborsiz qoldiradi, lekin bubbling qilmaydi (delegatsiya ishlamaydi).relatedTargetnullbo'lishi mumkin โ har doim tekshiring.