- 沒有父層物件的概念
- Event Bubbling
$(function(){以上圖結構與程式測試,滑鼠從 body 進入 DIV.a、DIV.b 與 DIV.c,再反向出來,得到以下結果。
$('div').mouseover(function(e){
log(e);
});
$('div').mouseout(function(e){
log(e);
});
});
function log(e) {
neil.log(e.type + '(T)' + e.target.tagName + '.' + e.target.id + '(C)' + e.currentTarget.tagName + '.' + e.currentTarget.id + '(R)' + e.relatedTarget.tagName + '.' + e.relatedTarget.id);
}
-- 進入 DIV.a
mouseover(T)DIV.a(C)DIV.a(R)BODY.
-- 進入 DIV.b
mouseout(T)DIV.a(C)DIV.a(R)DIV.b
mouseover(T)DIV.b(C)DIV.b(R)DIV.a
mouseover(T)DIV.b(C)DIV.a(R)DIV.a -- bubbling
-- 進入 DIV.c
mouseout(T)DIV.b(C)DIV.b(R)DIV.c
mouseout(T)DIV.b(C)DIV.a(R)DIV.c -- bubbling
mouseover(T)DIV.c(C)DIV.c(R)DIV.b
mouseover(T)DIV.c(C)DIV.b(R)DIV.b -- bubbling
mouseover(T)DIV.c(C)DIV.a(R)DIV.b -- bubbling
-- 離開 DIV.c
mouseout(T)DIV.c(C)DIV.c(R)DIV.b
mouseout(T)DIV.c(C)DIV.b(R)DIV.b -- bubbling
mouseout(T)DIV.c(C)DIV.a(R)DIV.b -- bubbling
mouseover(T)DIV.b(C)DIV.b(R)DIV.c
mouseover(T)DIV.b(C)DIV.a(R)DIV.c -- bubbling
-- 離開 DIV.b
mouseout(T)DIV.b(C)DIV.b(R)DIV.a
mouseout(T)DIV.b(C)DIV.a(R)DIV.a -- bubbling
mouseover(T)DIV.a(C)DIV.a(R)DIV.b
-- 離開 DIV.a
mouseout(T)DIV.a(C)DIV.a(R)BODY.
從 BODY 進入 DIV.a 比較沒有問題,奇怪的事情是從進入 DIV.b 開始,不只出現進入 DIV.b 的 mouseover,還出現離開 DIV.a 的 mouseout,離開 DIV.a 進入 DIV.b 嚴格來講是沒錯,但在實務運用上會造成一些困擾,畢竟還是在 DIV.a 裡面啊,另外還多了一個因為泡泡作用而出現的進入 DIV.a 的 mouseover。
好亂啊,多了一個離開 DIV.a,又多了一個進入 DIV.a,到底是進入 DIV.a 還是離開啊,雖然瀏覽器很聰明的知道要先呼叫 mouseout 再呼叫 mouseover,不然就真的離開 DIV.a 了。
接下來從 DIV.b 進入 DIV.c 又更精彩了,離開 DIV.b 進入 DIV.c 加上泡泡,很簡單的一個進入動作,結果產生 5 個 Event。
從 DIV.c 一路離開也是一樣的劇本再來一次,只是進入與離開動作對調吧了。
先來看看實務運用上的困擾,最常遇到的就是下拉選單,需求很簡單,游標移到「Menu」就彈出下拉選單,當游標移出下拉選單(橘色框)就關閉下拉選單。
先看 item 之間沒有留白的狀況。
// 顯示選單若沒有紅色部份的判斷,只要游標移到 item 裡,因為「進入a離開b」的邏輯,選單就會被隱藏,永遠也點不到 item。
$('div.menus').mouseover(function(e){
$('div.menu').show();
});
// 隱藏選單
$('div.menu').mouseout(function(e){
if ($(e.relatedTarget).parents('div.menu').length > 0) {
return;
}
$(this).hide();
});
當游標從下拉選單離開,可能是真的離開,也可能是移到下拉選單裡的 item,所以要避免後者的情況發生,除外邏輯就是去判斷離開選單進入的對象是不是選單的內層物件,若是,表示還在選單裡,這時就不可以隱藏選單,e.relatedTarget 指的就是「進入的對象」,然後用 parents() 判斷「進入的對象」與選單之間是否存在上下關係,若是則不隱藏表單。
再來看看 item 之間有留白的情況,與沒有留白的情況比較會多一個 Event 出現,就是從 menu item 離開進入 menu,進入 menu 沒事,問題出在離開 item 這個 Event 會因「泡泡作用」丟給 menu,導致 menu 多收到一個 mouseout,而之前加的除外邏輯必須為內外層關係才成立,而這時的 e.relatedTarget 指的就是 menu,所以除外邏輯派不上用場。
解決方法一,在除外邏輯加上 menu 的判斷。
$('div.menu').mouseout(function(e){解決方法二,制止 item 丟給 menu。
if (e.relatedTarget == this || $(e.relatedTarget).parents('div.menu').length > 0) {
return;
}
$(this).hide();
});
$('div.item').mouseout(function(e){
e.stopPropagation();
// or
return false;
});
mouseenter 與 mouseleave
jQuery 帶來更好的解決方法:mouseenter 與 mouseleave,不會有進入 a 就得離開 b 的問題,也不會泡泡往外丟。
$('div.menus').mouseenter(function(e){hover
$('div.menu').show();
});
$('div.menu').mouseleave(function(e){
$(this).hide();
});
jQuery 為 mouseenter 與 mouseleave 提供一個簡單的用法。
$('div.menus').hover(enterHandler, leaveHandler);
或者
$('div.menus').hover(enterAndLeaveHandler);
沒有留言:
張貼留言