js mouseover mouseout 多次觸發


問題:當鼠標移動到元素上,多次觸發mouseover,mouseout事件。

(注,該問題是在實現鼠標移動到一起菜單,滑動彈出二級時碰到的;因為鼠標移動到二級菜單時,動畫再次觸發,才意識到該問題;之前因為使用的是:hover偽類實現的顯示二級菜單,並且沒有加入動畫,所以並沒有發現該問題。)

問題原因分析:事件的冒泡機制,當子元素上發生相應事件時,會觸發父級元素的該事件。如A元素包含B元素,在A,B元素上分別添加mouseover,mouseout監聽事件,當鼠標移到A上,但不在B上時,觸發A的mouseover,同時對應的event.eventPhase為2即目標階段;當鼠標繼續移入B元素中時,這時觸發A事件的mouseout,event.eventPhase為3,即冒泡階段,同時觸發B事件的mouseover,event.eventPhase為2,即目標階段。

解決方法:檢測事件的相關元素(relatedTarget,對於mouseover來說:relatedTarget是鼠標進入元素前,所離開的元素;對於mouseout,relatedTarget是鼠標離開元素后,所進入的元素)是被綁定元素的子元素與否。(或者利用jQuery的mouseenter,mouseleave事件,因為jquery已經將該事件封裝)

function contains(parentNode, childNode) {
    if (parentNode.contains) {
        return parentNode != childNode && parentNode.contains(childNode);
    } else {
        return !!(parentNode.compareDocumentPosition(childNode) & 16);
    }
}

該函數是判斷兩個節點的關系,它考慮到IE與其他瀏覽器的兼容性,[dom].contains([dom])方法是IE瀏覽器的方法([dom]表示文檔流中的節點),[A].compareDocumentPosition([B])是DOM3中的方法,下面是不同位置關系對應的返回結果。

 Bits          Number        Meaning
000000         0              元素一致
000001         1              節點在不同的文檔(或者一個在文檔之外)
000010         2              節點 B 在節點 A 之前
000100         4              節點 A 在節點 B 之前
001000         8              節點 B 包含節點 A
010000         16             節點 A 包含節點 B
100000         32             瀏覽器的私有使用

接下來是判斷事件相關元素與目標元素之間的關系,只有當觸發事件的相關元素不是目標元素的后繼節點,checkHover()函數才返回true.

function checkHover(e,target){
    if (getEvent(e).type=="mouseover")  {
        return !contains(target,getEvent(e).relatedTarget||getEvent(e).fromElement) && !((getEvent(e).relatedTarget||getEvent(e).fromElement)===target);
    } else {
        return !contains(target,getEvent(e).relatedTarget||getEvent(e).toElement) && !((getEvent(e).relatedTarget||getEvent(e).toElement)===target);
    }
}

 

getEvent是為了兼容IE瀏覽器;checkHover函數中之所以添加一個if判斷是因為IE下mouseover和mouseout的相關元素分別對應的是fromElement,toElement,因此分別處理,當是其他事件時,這兩個屬性在IE下為null。而FF和chrome瀏覽器中的相關元素都是relatedTarget,mouseover中relatedTarget是鼠標移到目標元素時所離開的那個元素,mouseout中relatedTarget是鼠標離開目標元素時要進入的元素,對於其他事件該屬性無用。

最后是函數的調用。

myElement.onmouseover=function(e){
    if(checkHover(e,this)){
        do someting...
    }
}

 附加:

1)event還有兩個對象currentTarget,target;currentTarget是當前響應事件的對象,target是最初觸發該事件的對象;

2)阻止事件的冒泡為event.stopPropagation();

3)阻止默認行為為event.perventDefault();

4)在處理函數中最后寫上return false;即阻止冒泡又阻止默認操作。

5)事件分為3個階段:捕捉階段,eventPhase為1;目標階段,eventPhase為2;冒泡階段,eventPhase為3。

下面為測試時,監控程序的代碼

var count=0;
$(this).bind('mouseover',function(e){
        console.log(++count+' mouseover: '+e.target.className+" "+e.eventPhase);
        ...
    
}).bind('mouseout',function(e){
        console.log(++count+' mouseout: '+e.target.className+" "+e.eventPhase);
        ...
})

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM