跨瀏覽器的 mouseenter mouseleave 以及 compareDocumentPosition的使用說明
寫了這么久 js應(yīng)用 我居然不知道這兩個事件 于是 去google搜索了一番. 才發(fā)現(xiàn)這兩個事件 是如此的優(yōu)秀 且好用... 但搜索過程中 發(fā)現(xiàn) 好多人 似乎不太明白這兩個事件 和mouseover mouseout 真正的區(qū)別 和用途.. 并且看到google中搜索得到的 一些朋友 實現(xiàn)的 跨瀏覽器 解決方案. 覺得似乎有些繁瑣...所以產(chǎn)生了寫一篇blog 把這玩意 說透徹的沖動... 好啦.我們進(jìn)入正題.
對于 mouseover 和mouseenter 兩個事件 最大的區(qū)別就是 mouseenter 是 不冒泡的事件 ..這話怎么理解呢?
<div id=="parent">
<div id="child"></div>
</div>
對于mouseover 時間來說 當(dāng)鼠標(biāo)從其他元素 移動到 child節(jié)點時發(fā)生 但此事件會冒泡 所以會導(dǎo)致 parent 也出發(fā)mouseover
如果我們對 parent注冊了 mouseover監(jiān)聽. 則可能會產(chǎn)生一個什么問題呢? 從 parent移動到child 同樣出發(fā)parent的mouseover 有時候我們不希望這樣的事情發(fā)生. 這時候 如果注冊的監(jiān)聽 是mouseenter的話 無論鼠標(biāo)從任何元素 移動到child時 只有child元素 發(fā)生mouseenter事件 而其祖宗節(jié)點 都不會因為冒泡 而觸發(fā)此事件...這就 使我們可以徹底放棄 我們以往為了 實現(xiàn)同樣的邏輯 又要對子節(jié)點禁止mouseover冒泡 或者又去判斷事件源對象 或判斷srcElement/relatedTarget 那樣麻煩的方案.
對于 mouseout 和mouseleave 也是如此 當(dāng)鼠標(biāo)從child 移出時 mouseout同樣會冒泡到 parent 從而觸發(fā)parent的 mouseout 二mouseleave 同樣無此問題.
知道了區(qū)別 剩下的事情就好辦多了. 遇到此類需求 我們一律mouseenter mouseleave就好..問題是 這玩意只有ie支持 怎么辦呢?
我們只能 用mouseover 和mouseout來模擬 但是如果我們的模擬方案 太過復(fù)雜 那是在就意義不大了... 這時候我們就可以 借助 xml 方法compareDocumentPosition 來徹底解決這個問題
我在我的類庫中 封裝了一個方法 專門用來判斷 某個節(jié)點的位置 是否在另一個節(jié)點的子節(jié)點中...
ie可以用 parentNode.contains(childNode) 來判斷 這沒什么好說的 childNode在parentNode DOM樹中存在 那么就是true
而contains方法 ie專屬 那么 我們就是借助 !!(node.compareDocumentPosition(node2) &16) 來實現(xiàn)同樣的效果.
那么接下來 我們就來談?wù)?compareDocumentPosition 方法 否則 你看到上面的 &16 一定會困惑無比...
compareDocumentPosition 方法在非ie瀏覽器 都被實現(xiàn)到 節(jié)點對象的 中了 所以
node.compareDocumentPosition(node2) 的作用就是 比較 node節(jié)點與node2節(jié)點之間的位置關(guān)系..
他的返回值是一個number值...
一般來說 對我們有用的 是以下幾個值
1. 20 (2進(jìn)制: 010100)
2. 10 (2進(jìn)制: 001010)
3. 4 (2進(jìn)制: 000100)
4. 2 (2進(jìn)制: 000010)
5. 0 (2進(jìn)制: 000000)
6. 2進(jìn)100***的數(shù)...
那么這些 20 10 4 2 0 是怎么來的呢? 我們接著往下 看...
試試上 這個2進(jìn)制算法 是專門用來解釋 兩個節(jié)點之間的關(guān)系的
這個 6位2進(jìn)制數(shù) 才是根本所在
第6位 代表 兩個節(jié)點是否一個在DOM樹上一個不在 這個是針對整個DOM樹而言的.也就是說 如果兩個都不在 或兩個都在 則為0 否則為1
第5位 代表node是否是node2的父節(jié)點 如果是 則為1 否則為0
第4位 代表node是否是node2的子節(jié)點 如果是則為1 否則為0
第3位 代表node是否在node2的前面 如果是 則為1 否則為0 (注:如果node是node2的父節(jié)則node同時也看做在node2的前面)
第2位 代表node是否在node2的后面 如果是 則為1 否則為0 (注如果node是node2的子節(jié)點 則node同時也看做在node2的后面)
最后 如果 2 3 4 5 6 位 都為0 即 000000 說明 兩個節(jié)點 要么同時在DOM樹上 要么同時不在. 且 兩個節(jié)點 沒有任何關(guān)系 那么只有一種可能 即兩個節(jié)點是同一個節(jié)點...也就是 node==node2
所以 node.compareDocumentPosition(node2) &16 位運算 的結(jié)果是什么呢? 以上幾種可能的組合中只有 010100 &010000 以及 110*** &16 的結(jié)果是 010000 即返回16 其他情況 均返回 0 然后 用!! 吧number隱式轉(zhuǎn)型成boolean類型 我們就可以判斷出 node是否是 node2的父節(jié)點了...
所以 我們也可以使用 node2.compareDocumentPosition(node) && 8 來判斷node2 是不是 node的子節(jié)點 道理是同樣的
或者我們也可以直接 node.compareDocumentPosition(node2) ==20 來做判斷 這樣還可以省略 !! 做轉(zhuǎn)型..也是可以的.
到了這里 聰明你的 一定發(fā)現(xiàn) 這玩意是什么? 分明就是c#中 flag 標(biāo)識枚舉 的用法...
c# 中 File.Attributes 枚舉 可能同時具備 n多屬性 比如一個文件 可以是 只讀的同時 還可以是 隱藏的 或者同時還可以是 共享的. 等等
那么 用一個枚舉 值 如何確定 一個文件同時具備哪些屬性 又不產(chǎn)生沖突呢? 答案 于 compareDocumentPosition是一樣的...
我用js 實現(xiàn)了一個 類似邏輯 來管理 flag標(biāo)識的類 來說明這個問題 代碼如下
// flag 類
function flag(sFlags) {
this._flags = {};
this._status = 0;
sFlags && this.initFlags(sFlags);
}
flag.prototype = {
constructor: flag,
initFlags: function(sFlags) {//sFlags "狀態(tài)1,狀態(tài)2,狀態(tài)3...... 初始化原始標(biāo)識集合...
sFlags = sFlags.split(',');
for (var i = 0, len = sFlags.length; i < len; i++) this._flags[sFlags[i]] = 1 << i;
//這里初始化標(biāo)識的值 如果同時具備n種狀態(tài) 則 每種狀態(tài)的值一定是 000001 000010 000100 001000 010000 100000
},
setStatus: function(sFlags) { //sFlags "狀態(tài)1,狀態(tài)3......設(shè)置當(dāng)前狀態(tài)
sFlags = sFlags.split(',');
this._status=0;
for (var i = 0, len = sFlags.length; i < len; i++) {
this._check[sFlags[i]];
this._status |= this._flags[sFlags[i]];
}
},
addStatus: function(sFlags) {//sFlags "狀態(tài)1,狀態(tài)3......檢查當(dāng)前狀態(tài)標(biāo)示 是否有 狀態(tài)1和狀態(tài)3 如果沒有則添加
sFlags = sFlags.split(',');
for (var i = 0, len = sFlags.length; i < len; i++) {
this._check(sFlags[i]);
if (this.hasStatus(sFlags[i])) continue; //判斷是否已經(jīng)有這個狀態(tài)如果有 跳過.
this._status |= this._flags[sFlags[i]];
//當(dāng)前的狀態(tài)值 與 允許的標(biāo)識值 做 | 運算 即添加狀態(tài)
}
},
removeStatus: function(sFlags) {
sFlags = sFlags.split(',');
for (var i = 0, len = sFlags.length; i < len; i++) {
this._check(sFlags[i]);
if (!this.hasStatus(sFlags[i])) continue;
this._status ^= this._flags[sFlags[i]];
// 當(dāng)前狀態(tài)值 與 要去掉的狀態(tài)值做 ^運算 即刪除狀態(tài)
}
},
clear: function() {
this._status = 0;
},
hasStatus: function(sFlags) {//sFlags "狀態(tài)1,狀態(tài)3.....狀態(tài) n.檢查當(dāng)前狀態(tài)標(biāo)識 是否同時 具備狀態(tài)1和狀態(tài)3 以及狀態(tài)n
sFlags = sFlags.split(',');
for (var i = 0, len = sFlags.length; i < len; i++) {
this._check(sFlags[i]);
if ((this._status & this._flags[sFlags[i]]) != this._flags[sFlags[i]]) return false;
//當(dāng)前狀態(tài)值 與輸入的狀態(tài)做 & 運算 如果返回值 不等于 改狀態(tài) 的標(biāo)識值 則 return false .
//比如 010101 & 010000 返回010000則 說明當(dāng)前標(biāo)識值具備 010000這個狀態(tài).
}
return true;
},
_check: function(sFlag) {
if (!sFlag in this._flags) throw new Error(" 當(dāng)前 flag 中不存在" + sFlag + "標(biāo)識");
//檢查當(dāng)前輸入狀態(tài)字符串 是否是合法值.
}
}
用法:
var fileStatus=new flag('readOnly,hidden,otherStatus');
fileStatus.setStatus('readOnly,hidden');
alert(fileStatus.hasStatus('readOnly'))//true;
alert(fileStatus.hasStatus('hidden'))//true;
alert(fileStatus.hasStatus('otherStatus'))//false;
最后 我們回到正題 我們借助 compareDocumentPosition 來模擬 mouseenter mouseleave
DOM結(jié)構(gòu):
<div id="dd" style="background-color:#369;width:50%;height:50%;position:absolute;left:25%;top:25%;" >
<div style="background-color:#ff0;width:50%;height:50%;position:relative;left:25%;top:25%" >
<div style="background-color:#789;width:50%;height:50%;position:relative;left:25%;top:25%" >
<div style="background-color:#123;width:50%;height:50%;position:relative;left:25%;top:25%" >
<div style="background-color:#456;width:50%;height:50%;position:relative;left:25%;top:25%" >
</div>
</div>
</div>
</div>
</div>
js腳本:
var dd = document.getElementById('dd')
if (! +'\v1') {//ie
dd.onmouseenter = function() { alert(1); };
dd.onmouseleave = function() { alert(2); };
}
else {//others
dd.onmouseover = function(e) {
var t = e.relatedTarget;
var t2 = e.target;
this == t2 && t && !(t.compareDocumentPosition(this) & 8) && alert(1);
};
dd.onmouseout = function(e) {
var t = e.relatedTarget;
var t2 = e.target;
this == t2 && t && !(t.compareDocumentPosition(this) & 8) && alert(2);
};
}
大功告成!!!!!
- 淺談JQ中mouseover和mouseenter的區(qū)別
- 關(guān)于事件mouseover ,mouseout ,mouseenter,mouseleave的區(qū)別
- 快速移動鼠標(biāo)觸發(fā)問題及解決方法(ECharts外部調(diào)用保存為圖片操作及工作流接線mouseenter和mouseleave)
- Jquery利用mouseenter和mouseleave實現(xiàn)鼠標(biāo)經(jīng)過彈出層且可以點擊
- 為非IE瀏覽器添加mouseenter,mouseleave事件的實現(xiàn)代碼
- javascript 兼容FF的onmouseenter和onmouseleave的代碼
- javascript中mouseenter與mouseover的異同
相關(guān)文章
理解Javascript_11_constructor實現(xiàn)原理
在理解了'對象模型'后,我們就可以看一下constructor屬性是如何實現(xiàn)的.2010-10-10JS實現(xiàn)可縮放、拖動、關(guān)閉和最小化的浮動窗口完整實例
這篇文章主要介紹了JS實現(xiàn)可縮放、拖動、關(guān)閉和最小化的浮動窗口的方法,實例分析了javascript操作窗口層的技巧,需要的朋友可以參考下2015-03-03xmlplus組件設(shè)計系列之分隔框(DividedBox)(8)
xmlplus 是一個JavaScript框架,用于快速開發(fā)前后端項目。這篇文章主要介紹了xmlplus布局類組件之分隔框,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05Ajax異步文件上傳與NodeJS express服務(wù)端處理
本文主要介紹了Ajax異步文件上傳與NodeJS express服務(wù)端處理的相關(guān)知識。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04JS中LocalStorage與SessionStorage五種循序漸進(jìn)的使用方法
這篇文章主要介紹了JS中LocalStorage與SessionStorage五種循序漸進(jìn)的使用方法,需要的朋友可以參考下2017-07-07原生Js實現(xiàn)按的數(shù)據(jù)源均分時間點幻燈片效果(已封裝)
騰訊新聞詳情頁有一個事件進(jìn)展效果, 覺得挺有意思. 于是, 就有了本文的效果: 按數(shù)據(jù)源均分時間點幻燈. 花了三個多小時寫的, 當(dāng)然, 包括樣式與調(diào)試. 兼容主流。2010-12-12關(guān)于JavaScript回調(diào)函數(shù)的深入理解
由于函數(shù)是一等對象,我們可以把一個函數(shù)作為參數(shù)傳遞給另一個函數(shù),然后在那個函數(shù)內(nèi)執(zhí)行,至也可以被那個函數(shù)返回,然后再執(zhí)行,這篇文章主要給大家介紹了關(guān)于JavaScript回調(diào)函數(shù)的深入理解,需要的朋友可以參考下2021-06-06