問題描述
我希望當鼠標移動到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"><div id="id2"></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"><div id="id2"></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中是否包含bfunction 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"><div id="id2"></div></div><script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script><script type="text/javascript">//判斷兩個a中是否包含bfunction 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"><div id="id2"></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多次觸發問題如何解決的相關內容,希望對大家有所幫助!