問題描述
我希望當鼠標移動到id1上的時候,id2顯示,當鼠標離開id1的時候,id2顯示。問題如下:
1.當鼠標從id1上移動到id2上的時候,id由有顯示變為不顯示,然後變為顯示
2.當鼠標從id2上移動到id1上的時候, id2有顯示變為不顯示,然後變為顯示
我希望的是當鼠標在id1或者id2上移動的時候,id2一直顯示,不發生變化。
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script> <div id="id1" style="width:800px; height:400px; background-color:#F23"> <div id="id2" style="width:400px; height:300px; background-color:#0F8; display:none;"> </div> </div> <script type="text/javascript"> $("#id1").mouseover(function(){ $(this).children().fadeIn(1000); }).mouseout(function(){ $(this).children().fadeOut(1000); }); </script>
問題解決辦法
最開始的問題分析,當鼠標從id1上移動到id2上的時候,由於鼠標由id2離開進入id1,針對id1觸發了一個mouseout事件,於是id2有顯示變為不顯示,接著在鼠標移動到id2上,在id2上觸發了一個mouseover事件,由於冒泡機制,id2上的mouseover冒泡到id1之前,觸發了id1上的mouseover事件,然後id2由不顯示變為顯示。同理,當鼠標從id2上移動到id1上的時候,針對id2,觸發了一個mouseout事件,還是因為冒泡機制,mouseout事件傳到id1上,id2由顯示變為不顯示,接著鼠標移動到id1之前,觸發了一個mouseover事件,然後id2有不顯示變為顯示。
看來,上面的問題歸根要解決的是,當鼠標由id1上移動到id2上的時候,阻止id1的mouseout事件;當鼠標從id2上移動到id1上的時候,阻止id2的mouseout事件冒泡到id1之上。那麼僅僅通過阻止冒泡是解決不了問題。
為了解決這樣的問題,jQuery提供了mouseenter和mouseleave方法。於是將JS代碼改為如下,很好解決了問題。
$("#id1").mouseenter(function(){ $(this).children().fadeIn(1000); }).mouseleave(function(){ $(this).children().fadeOut(1000); });
很多地方都有介紹mouseenter、mouseleave與mouseover、mouseout,於是復制粘貼了一個。
/*********************************************************/
1.mouseover與mouseenter
不論鼠標指針穿過被選元素或其子元素,都會觸發 mouseover 事件。
只有在鼠標指針穿過被選元素時,才會觸發 mouseenter 事件。
2.mouseout與mouseleave
不論鼠標指針離開被選元素還是任何子元素,都會觸發 mouseout 事件。
只有在鼠標指針離開被選元素時,才會觸發 mouseleave 事件。
/*********************************************************/
現象確實是這個現象,但是過程說的有點模糊,我的理解如下:
當鼠標指針移動到被選元素,會觸發 mouseover 事件,這個大家都知道,當鼠標指針由被選元素移動到其子元素,先是觸發被選元素的mouseout事件,然後子元素的mouseover事件冒泡到被選元素,此時相當於被選元素先執行了一個mouseout事件,然後執行了一個mouseover事件。
為了驗證將代碼改為如下
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script> <div id="id1" style="width:800px; height:400px; background-color:#F23"> <div id="id2" style="width:400px; height:300px; background-color:#0F8; position:absolute; top:300px;"> </div> </div> <script type="text/javascript"> $("#id1").mouseover(function(){ //$(this).children().fadeIn(1000); console.log('a'); }).mouseout(function(){ //$(this).children().fadeOut(1000); console.log('b'); }); </script>
鼠標從頁面移動到id1,然後由id1移動到id2上,控制台輸出如下圖
可以看出id1先後調用了mouseover、mouseout、mouseover事件,正好和上面分析的相同。
mouseenter與mouseleave實現分析
原理分析
從上面分析,我們可以看出,要實現mouseenter與mouseleave的效果,就是當鼠標從被選元素移動到其子元素上的時候,被選元素不執行mouseout事件,也不執行子類冒泡過來的mouseover事件,當鼠標從被選元素子元素移動到被選元素上的時候,被選元素不執行mouseover事件,也不執行子類冒泡過來的mouseout事件。
要實現上面的效果,我們需要event對象的一個屬性relatedTarget,這個屬性就是用來判斷 mouseover和mouseout事件目標節點的相關節點的屬性。簡單的來說就是當觸發mouseover事件時,relatedTarget屬性代表的就是鼠標剛剛離開的那個節點,當觸發mouseout事件時它代表的是鼠標移向的那個對象。由於MSIE不支持這個屬性,不過它有代替的屬性,分別是 fromElement和toElement。除此,我們還需要contains方法,來判斷一個對象是否包含在另外一個對象中。
這樣當鼠標移動,需要判斷以下兩條即可
1.調用mouseover,只需要判斷relatedTarget是否被選元素的子元素,如果是,則不執行(當於從被選元素子元素移動到被選元素,不執行mouseover;當於從被選元素移動到被選元素子元素,不執行冒泡過來的mouseover);
2.調用mouseout,只需要判斷relatedTarget是否被選元素的子元素,如果是,則不執行(當於從被選元素子元素移動到被選元素,不執行子元素冒泡過來的mouseout;當於從被選元素移動到被選元素子元素,不執行mouseover);
實現過程
判斷兩個元素是否存在包含關系
jquery中封裝了contains函數如下
可以簡化為如下
//判斷兩個a中是否包含b function contains(a,b){ return a.contains ? a != b && a.contains(b) :!!(a.compareDocumentPosition(b) & 16); }
compareDocumentPosition介紹
這個方法是 DOM Level 3 specification 的一部分,允許你確定 2 個 DOM Node 之間的相互位置。這個方法比 .contains() 強大。這個方法的一個可能應用是排序 DOM Node 成一個詳細精確的順序。NodeA.compareDocumentPosition(NodeB)返回的信息描述如下:
比特 序號 意義
通過上面我們就可以理解為什麼要寫成a.compareDocumentPosition(b) & 16因為如果節點 A 包含節點 B,就會返回16,16&16=1,其他的情況結果都會0。
獲取兼容性性的relatedTarget
為了兼容各種浏覽器,參考jquery源碼,寫出如下代碼,來獲取mouseover和mouseout事件目標節點的相關節點的屬性relatedTarget。
function getRelated(e){ var related; var type=e.type.toLowerCase();//這裡獲取事件名字 if(type=='mouseover'){ related=e.relatedTarget||e.fromElement }else if(type='mouseout'){ related=e.relatedTarget||e.toElement } return related; }
改進mouseover和mouseout
改進mouseover和mouseout以實現改進mouseenter與mouseleave效果,所有代碼如下。
<!DOCTYPE html> <html> <head> <title></title> </head> <body> <div id="id1" style="width:800px; height:400px; background-color:#F23"> <div id="id2" style="width:400px; height:300px; background-color:#0F8; position:absolute; top:300px;"> </div> </div> <script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script> <script type="text/javascript"> //判斷兩個a中是否包含b function contains(a,b){ return a.contains ? a != b && a.contains(b) :!!(a.compareDocumentPosition(b) & 16); } function getRelated(e){ var related; var type=e.type.toLowerCase();//這裡獲取事件名字 if(type=='mouseover'){ related=e.relatedTarget||e.fromElement }else if(type='mouseout'){ related=e.relatedTarget||e.toElement } return related; } $(function(){ $("#id1").mouseover(function(e){ //判斷鼠標從哪移到id1上面 var related=getRelated(e); //如果related是id1的子元素id2,即從子元素id2移動到id1,或是related為id1,即從id1移動到其子元素id2上面,則不進行任何操作,否則進行相應的操作 if(this!=related && !contains(this,related)){ console.log('mouseover'); } }).mouseout(function(e){ //判斷鼠標要從id1上面移動到哪去? var related=getRelated(e); //如果related是id1,即當id1從其子元素移動到id1上,或是related是id2,即從id1上移動到其子元素,不進行任何操作,否則進行相應的操作 if(this !=related && !contains(this,related)){ console.log('mouseout'); } }); }); </script> </body> </html>
測試,鼠標移動路線如下圖路線
由控制台可以很看出,此刻的mouseover和mouseout已經完全具備mouseenter與mouseleave效果效果。
代碼的封裝
如果每次進行這樣的操作,都需要加載Jquery或是寫很多代表,將是件繁瑣的事,為了便於以後操作,進行了適當的封裝,模擬Jquery,生成自己的mouseenter與mouseleave。代碼封裝到dqMouse.js文件中,如下:
(function(w){ var dqMouse = function(obj) { // 函數體 return new dqMouse.fn.init(obj); } dqMouse.fn = dqMouse.prototype = { // 擴展原型對象 obj:null, dqMouse: "1.0.0", init: function(obj) { this.obj=obj; return this; }, contains:function(a,b) { return a.contains ? a != b && a.contains(b) :!!(a.compareDocumentPosition(b) & 16); }, getRelated:function(e) { var related; var type=e.type.toLowerCase();//這裡獲取事件名字 if(type=='mouseover'){ related=e.relatedTarget||e.fromElement }else if(type='mouseout'){ related=e.relatedTarget||e.toElement } return related; }, over:function(fn){ var obj=this.obj; var _self=this; obj.onmouseover=function(e){ var related=_self.getRelated(e); if(this!=related && !_self.contains(this,related)){ fn(); } } return _self; }, out:function(fn){ var obj=this.obj; var _self=this; obj.onmouseout=function(e){ var related=_self.getRelated(e); if(obj!=related && !_self.contains(obj,related)){ fn(); } } return _self; } } dqMouse.fn.init.prototype = dqMouse.fn; window.dqMouse = window.$$= dqMouse; })(window);
調用的源文件如下:
<div id="id1" style="width:800px; height:400px; background-color:#F23"> <div id="id2" style="width:400px; height:300px; background-color:#0F8; position:absolute; top:300px;"> </div> </div> <script type="text/javascript" src="dqMouse.js"></script> <script type="text/javascript"> var id1=document.getElementById('id1'); $$(id1).over(function(){ console.log('mouseover'); }).out(function(){ console.log('mouseout'); }); </script>
以上所述是小編給大家介紹的JS中mouseover和mouseout多次觸發問題如何解決的相關內容,希望對大家有所幫助!