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