JavaScript中,父元素包含子元素:
當父級設置onmouseover及onmouseout時,鼠標從父級移入子級,則觸發父級的onmouseout后又觸發onmouseover;從子級移入父級后再次觸發父級的oumouseout后又觸發onmouseover。而如果onmouseover內又應用了計時器便會存在較大的問題。下面針對此問題給出解決方案。
首先,在給出解決方案之前,必須先弄清楚幾個對象及方法,分別如下:
1、事件對象
2、事件對象相關屬性(只針對onmouseover及onmouseout),即fromElement、toElement、relatedTarget
3、判斷一個元素是否包含另一個元素的方法,即element.contains(Node)與element.compareDocumentPosition(Node)
既然前面已經說了要弄清如上幾個對象及方法,那么,我們就可以分析一下倒底如何去解決這個問題。
分析:
存在的問題是設置onmouseover時,鼠標從移入父級內時,沒任何問題,但由父級移入子級時,以及由子級稱出到父級時會出現如上問題,那么我們可以想辦法設置當鼠標從父級移入到子級時,或從子級移出到父級時讓觸發對象失效,我們可以通過if語句進行判斷。而事件對象里面有個屬性可以獲取移入移出時的相關對象,下面就來介紹。
1、事件對象:可以獲取事件對象的一系列屬性,在事件中寫一個參數,即可通過參數獲取,方法如下:
1 wrap.onmouseover = function(e) { 2 e = window.event || e; // window.event是為了兼容ie下獲取事件對象,而e為標准瀏覽器直接獲取 3 }
2、事件對象的相關對象
在觸發onmouseover及onmouseout時,必定會涉及到其它對象,如:onmouseover的相關對象,即為哪個對象進入的。onmouseout的相關對象即為進入到哪個對象。獲取方法如下:
1 wrap.onmouseover = function(e) { 2 e = window.event || e; 3 var s = e.fromElement || e.relatedTarget; //e.fromElement為IE下onmouseover獲取相關對象方法,relatedTarget為標准瀏覽器下獲取方法 4 } 5 wrap.onmouseout = function(e) { 6 e = window.event || e; 7 var s = e.toElement || e.relatedTarget; //e.toElementIE下onmouseout獲取相關對象方法,relatedTarget為標准瀏覽器下獲取方法 8 }
3、判斷一個元素是否包含另一個元素
IE下可以使用a.contains(b)判斷a是否包含b
標准瀏覽器下a.compareDocumentPosition(b)有5個值,若為0表示為同一節點,若為2表示a位於b后面,若為4表示a位於b前面,若為10表示a為b的后代,若為20表示a為b的祖級。
先上個即將用到的示例的HTML及CSS
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <style type="text/css"> 6 html,body,div{margin:0;padding:0} 7 .wrapper { 8 overflow: hidden; 9 width: 800px; 10 background: black; 11 } 12 .box { 13 height: 200px; 14 background: #FF0; 15 margin: 80px 0; 16 } 17 </style> 18 </head> 19 <body> 20 <div class="wrapper" id="wrap"> 21 <div class="box" id="box"></div> 22 </div> 23 </body> 24 </html>
既然知道了以上的獲取屬性及事件的必備方法,那么我們就可以想方法解決問題了
當觸發onmouseover時,可能是從對象以外移入的,也有可能是父級移入到子級,以及子級移出到父級,剛才也說過,onmouseover的相關對象是獲取從哪個對象進入的。如果是從外面的對象進入的,我們就執行所需的代碼。如果是從父級移入到子級或是由子級移出到父級時,則直接跳過。
父級移入到子級對象,相關對象為父級。 子級移出到父級對象,相對對象為子級。
代碼如下:
1 wrap.onmouseover = function(e) { 2 e = window.event || e; 3 var s = e.fromElement || e.relatedTarget; 4 if (document.all) { //判斷瀏覽器是否為IE,如果存在document.all則為IE 5 if (!this.contains(s)) { // 判斷調用onmouseover的對象(this)是否包含自身或子級,如果包含,則不執行 6 console.log('IE will over'); 7 } 8 } else { //標准瀏覽器下的方法 9 var reg = this.compareDocumentPosition(s); 10 if (!(reg == 20 || reg == 0)) { 11 console.log('Browser will over'); 12 } 13 } 14 }
當觸發onmouseout時,可能是從父級移到子級,也可能由子級移到父級,或是移出至父級之外。
父級稱到子級,則相關對象為子級,子級稱到父級,則相關對象為父級。
代碼如下:
1 wrap.onmouseout = function(e) { 2 e = window.event || e; 3 var s = e.toElement || e.relatedTarget; 4 if(document.all) { 5 if (!this.contains(s)) { 6 console.log('IE will out'); 7 } 8 } else { 9 var reg = this.compareDocumentPosition(s); 10 if (!(reg == 20 || reg == 0)) { 11 console.log('Browser will out'); 12 } 13 } 14 }
問題也就得到了解決。
不過你會發現:onmouseover與onmouseout的判斷方法其實是一樣的。於是我們得到:
無論是移入還是移出,只要相關對象是父級以外的就可以執行,否則代碼不執行。
最后,再為大家提供本人自己寫的兼容代碼,復制到JS代碼后可直接調用。代碼如下:
1 /* 2 * 功能:鼠標移入對象觸發事件,兼容所有瀏覽器並防止鼠標在父子對象間移動造成的觸發onmouseover的bug 3 * 參數:第一個參數表示觸發的對象,第二個參數表示觸發的對象的事件對象,第三個對象表示要執行的函數 4 * 作者:http://www.cnblogs.com/wuyuchang/ 5 */ 6 function mouseover(a, e, func) { 7 e = e || window.event; 8 var b = e.fromElement || e.relatedTarget; 9 10 mouseoverAndOut(a, b, func); 11 } 12 /* 13 * 功能:鼠標移出對象觸發事件,兼容所有瀏覽器並防止鼠標在父子對象間移動造成的onmouseout的bug 14 * 參數:第一個參數表示觸發的對象,第二個參數表示觸發的對象的事件對象,第三個對象表示要執行的函數 15 * 作者:http://www.cnblogs.com/wuyuchang/ 16 */ 17 function mouseout(a ,e, func) { 18 e = e || window.event; 19 var b = e.toElement || e.relatedTarget; 20 mouseoverAndOut(a, b, func); 21 } 22 /* 23 * 功能:鼠標移入或移出對象觸發事件,兼容所有瀏覽器並防止鼠標在父子對象間移動造成的onmouseover & onmouseout的bug 24 * 參數:第一個參數表示觸發的對象,第二個參數表示觸發的對象的事件對象,第三個對象表示要執行的函數 25 * 作者:http://www.cnblogs.com/wuyuchang/ 26 */ 27 function mouseoverOrOut(a, e, func) { 28 e = e || window.event; 29 var b; 30 if (e.type == 'mouseover') { 31 b = e.fromElement || e.relatedTarget; 32 } else if (e.type == 'mouseout') { 33 b = e.toElement || e.relatedTarget; 34 } 35 mouseoverAndOut(a, e, func); 36 } 37 /* 38 * 功能:鼠標移入或移出對象觸發事件,兼容所有瀏覽器並防止鼠標在父子對象間移動造成的onmouseover & onmouseout的bug 39 * 參數:第一個參數表示觸發的對象,第二個參數表示觸發的對象的相關對象,第三個對象表示要執行的函數 40 * 作者:http://www.cnblogs.com/wuyuchang/ 41 */ 42 function mouseoverAndOut(a, b, func) { 43 if (document.all) { 44 if (!(a.contains(b))) { 45 func(); 46 } 47 } else { 48 var res = a.compareDocumentPosition(b); 49 if(!(res == 20 || res == 0)){ 50 func(); 51 } 52 } 53 }
(如果是onmouseover,調用mouseover(a, e, func)即可;如果是onmouseout,調用mouseout(a, e, func)即可;或不管onmouseover還是onmouseout直接調用mouseoverOrOut(a, e, func)即可。)
以上為個人經驗之談,如有不到之處還請留言指點。