JS中mouseover和mouseout多次觸發(fā)問題如何解決
問題描述
我希望當(dāng)鼠標(biāo)移動(dòng)到id1上的時(shí)候,id2顯示,當(dāng)鼠標(biāo)離開id1的時(shí)候,id2顯示。問題如下:
1.當(dāng)鼠標(biāo)從id1上移動(dòng)到id2上的時(shí)候,id由有顯示變?yōu)椴伙@示,然后變?yōu)轱@示
2.當(dāng)鼠標(biāo)從id2上移動(dòng)到id1上的時(shí)候, id2有顯示變?yōu)椴伙@示,然后變?yōu)轱@示
我希望的是當(dāng)鼠標(biāo)在id1或者id2上移動(dòng)的時(shí)候,id2一直顯示,不發(fā)生變化。
<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>
問題解決辦法
最開始的問題分析,當(dāng)鼠標(biāo)從id1上移動(dòng)到id2上的時(shí)候,由于鼠標(biāo)由id2離開進(jìn)入id1,針對(duì)id1觸發(fā)了一個(gè)mouseout事件,于是id2有顯示變?yōu)椴伙@示,接著在鼠標(biāo)移動(dòng)到id2上,在id2上觸發(fā)了一個(gè)mouseover事件,由于冒泡機(jī)制,id2上的mouseover冒泡到id1之前,觸發(fā)了id1上的mouseover事件,然后id2由不顯示變?yōu)轱@示。同理,當(dāng)鼠標(biāo)從id2上移動(dòng)到id1上的時(shí)候,針對(duì)id2,觸發(fā)了一個(gè)mouseout事件,還是因?yàn)槊芭輽C(jī)制,mouseout事件傳到id1上,id2由顯示變?yōu)椴伙@示,接著鼠標(biāo)移動(dòng)到id1之前,觸發(fā)了一個(gè)mouseover事件,然后id2有不顯示變?yōu)轱@示。
看來,上面的問題歸根要解決的是,當(dāng)鼠標(biāo)由id1上移動(dòng)到id2上的時(shí)候,阻止id1的mouseout事件;當(dāng)鼠標(biāo)從id2上移動(dòng)到id1上的時(shí)候,阻止id2的mouseout事件冒泡到id1之上。那么僅僅通過阻止冒泡是解決不了問題。
為了解決這樣的問題,jQuery提供了mouseenter和mouseleave方法。于是將JS代碼改為如下,很好解決了問題。
$("#id1").mouseenter(function(){ $(this).children().fadeIn(1000); }).mouseleave(function(){ $(this).children().fadeOut(1000); });
很多地方都有介紹mouseenter、mouseleave與mouseover、mouseout,于是復(fù)制粘貼了一個(gè)。
/*********************************************************/
1.mouseover與mouseenter
不論鼠標(biāo)指針穿過被選元素或其子元素,都會(huì)觸發(fā) mouseover 事件。
只有在鼠標(biāo)指針穿過被選元素時(shí),才會(huì)觸發(fā) mouseenter 事件。
2.mouseout與mouseleave
不論鼠標(biāo)指針離開被選元素還是任何子元素,都會(huì)觸發(fā) mouseout 事件。
只有在鼠標(biāo)指針離開被選元素時(shí),才會(huì)觸發(fā) mouseleave 事件。
/*********************************************************/
現(xiàn)象確實(shí)是這個(gè)現(xiàn)象,但是過程說的有點(diǎn)模糊,我的理解如下:
當(dāng)鼠標(biāo)指針移動(dòng)到被選元素,會(huì)觸發(fā) mouseover 事件,這個(gè)大家都知道,當(dāng)鼠標(biāo)指針由被選元素移動(dòng)到其子元素,先是觸發(fā)被選元素的mouseout事件,然后子元素的mouseover事件冒泡到被選元素,此時(shí)相當(dāng)于被選元素先執(zhí)行了一個(gè)mouseout事件,然后執(zhí)行了一個(gè)mouseover事件。
為了驗(yàn)證將代碼改為如下
<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>
鼠標(biāo)從頁面移動(dòng)到id1,然后由id1移動(dòng)到id2上,控制臺(tái)輸出如下圖
可以看出id1先后調(diào)用了mouseover、mouseout、mouseover事件,正好和上面分析的相同。
mouseenter與mouseleave實(shí)現(xiàn)分析
原理分析
從上面分析,我們可以看出,要實(shí)現(xiàn)mouseenter與mouseleave的效果,就是當(dāng)鼠標(biāo)從被選元素移動(dòng)到其子元素上的時(shí)候,被選元素不執(zhí)行mouseout事件,也不執(zhí)行子類冒泡過來的mouseover事件,當(dāng)鼠標(biāo)從被選元素子元素移動(dòng)到被選元素上的時(shí)候,被選元素不執(zhí)行mouseover事件,也不執(zhí)行子類冒泡過來的mouseout事件。
要實(shí)現(xiàn)上面的效果,我們需要event對(duì)象的一個(gè)屬性relatedTarget,這個(gè)屬性就是用來判斷 mouseover和mouseout事件目標(biāo)節(jié)點(diǎn)的相關(guān)節(jié)點(diǎn)的屬性。簡單的來說就是當(dāng)觸發(fā)mouseover事件時(shí),relatedTarget屬性代表的就是鼠標(biāo)剛剛離開的那個(gè)節(jié)點(diǎn),當(dāng)觸發(fā)mouseout事件時(shí)它代表的是鼠標(biāo)移向的那個(gè)對(duì)象。由于MSIE不支持這個(gè)屬性,不過它有代替的屬性,分別是 fromElement和toElement。除此,我們還需要contains方法,來判斷一個(gè)對(duì)象是否包含在另外一個(gè)對(duì)象中。
這樣當(dāng)鼠標(biāo)移動(dòng),需要判斷以下兩條即可
1.調(diào)用mouseover,只需要判斷relatedTarget是否被選元素的子元素,如果是,則不執(zhí)行(當(dāng)于從被選元素子元素移動(dòng)到被選元素,不執(zhí)行mouseover;當(dāng)于從被選元素移動(dòng)到被選元素子元素,不執(zhí)行冒泡過來的mouseover);
2.調(diào)用mouseout,只需要判斷relatedTarget是否被選元素的子元素,如果是,則不執(zhí)行(當(dāng)于從被選元素子元素移動(dòng)到被選元素,不執(zhí)行子元素冒泡過來的mouseout;當(dāng)于從被選元素移動(dòng)到被選元素子元素,不執(zhí)行mouseover);
實(shí)現(xiàn)過程
判斷兩個(gè)元素是否存在包含關(guān)系
jquery中封裝了contains函數(shù)如下
可以簡化為如下
//判斷兩個(gè)a中是否包含b function contains(a,b){ return a.contains ? a != b && a.contains(b) :!!(a.compareDocumentPosition(b) & 16); }
compareDocumentPosition介紹
這個(gè)方法是 DOM Level 3 specification 的一部分,允許你確定 2 個(gè) DOM Node 之間的相互位置。這個(gè)方法比 .contains() 強(qiáng)大。這個(gè)方法的一個(gè)可能應(yīng)用是排序 DOM Node 成一個(gè)詳細(xì)精確的順序。NodeA.compareDocumentPosition(NodeB)返回的信息描述如下:
比特 序號(hào) 意義
通過上面我們就可以理解為什么要寫成a.compareDocumentPosition(b) & 16因?yàn)槿绻?jié)點(diǎn) A 包含節(jié)點(diǎn) B,就會(huì)返回16,16&16=1,其他的情況結(jié)果都會(huì)0。
獲取兼容性性的relatedTarget
為了兼容各種瀏覽器,參考jquery源碼,寫出如下代碼,來獲取mouseover和mouseout事件目標(biāo)節(jié)點(diǎn)的相關(guān)節(jié)點(diǎn)的屬性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; }
改進(jìn)mouseover和mouseout
改進(jìn)mouseover和mouseout以實(shí)現(xiàn)改進(jìn)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"> //判斷兩個(gè)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){ //判斷鼠標(biāo)從哪移到id1上面 var related=getRelated(e); //如果related是id1的子元素id2,即從子元素id2移動(dòng)到id1,或是related為id1,即從id1移動(dòng)到其子元素id2上面,則不進(jìn)行任何操作,否則進(jìn)行相應(yīng)的操作 if(this!=related && !contains(this,related)){ console.log('mouseover'); } }).mouseout(function(e){ //判斷鼠標(biāo)要從id1上面移動(dòng)到哪去? var related=getRelated(e); //如果related是id1,即當(dāng)id1從其子元素移動(dòng)到id1上,或是related是id2,即從id1上移動(dòng)到其子元素,不進(jìn)行任何操作,否則進(jìn)行相應(yīng)的操作 if(this !=related && !contains(this,related)){ console.log('mouseout'); } }); }); </script> </body> </html>
測(cè)試,鼠標(biāo)移動(dòng)路線如下圖路線
由控制臺(tái)可以很看出,此刻的mouseover和mouseout已經(jīng)完全具備mouseenter與mouseleave效果效果。
代碼的封裝
如果每次進(jìn)行這樣的操作,都需要加載Jquery或是寫很多代表,將是件繁瑣的事,為了便于以后操作,進(jìn)行了適當(dāng)?shù)姆庋b,模擬Jquery,生成自己的mouseenter與mouseleave。代碼封裝到dqMouse.js文件中,如下:
(function(w){ var dqMouse = function(obj) { // 函數(shù)體 return new dqMouse.fn.init(obj); } dqMouse.fn = dqMouse.prototype = { // 擴(kuò)展原型對(duì)象 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);
調(diào)用的源文件如下:
<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多次觸發(fā)問題如何解決的相關(guān)內(nèi)容,希望對(duì)大家有所幫助!
相關(guān)文章
JS實(shí)現(xiàn)左右拖動(dòng)改變內(nèi)容顯示區(qū)域大小的方法
這篇文章主要介紹了JS實(shí)現(xiàn)左右拖動(dòng)改變內(nèi)容顯示區(qū)域大小的方法,涉及JavaScript實(shí)時(shí)響應(yīng)鼠標(biāo)事件動(dòng)態(tài)改變頁面元素屬性的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10微信小程序開發(fā)之全局配置與頁面配置實(shí)現(xiàn)
本文主要介紹了微信小程序開發(fā)之全局配置與頁面配置實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07js手機(jī)號(hào)批量滾動(dòng)抽獎(jiǎng)實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了js手機(jī)號(hào)批量滾動(dòng)抽獎(jiǎng)實(shí)現(xiàn)代碼,s適用于年會(huì)等活動(dòng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01JavaScript常用數(shù)組去重實(shí)戰(zhàn)源碼
本文給大家分享js常用8種數(shù)組去重實(shí)戰(zhàn)源碼,針對(duì)每種方法通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-07-07firefo xml 讀寫實(shí)現(xiàn)js代碼
firefo xml 讀寫實(shí)現(xiàn) 不過要是你的xml要編碼成功還得在str前面加上xml頭,千萬別忘了啊。2009-06-06