寫了這么久 js應(yīng)用 我居然不知道這兩個(gè)事件 于是 去google搜索了一番. 才發(fā)現(xiàn)這兩個(gè)事件 是如此的優(yōu)秀 且好用... 但搜索過程中 發(fā)現(xiàn) 好多人 似乎不太明白這兩個(gè)事件 和mouseover mouseout 真正的區(qū)別 和用途.. 并且看到google中搜索得到的 一些朋友 實(shí)現(xiàn)的 跨瀏覽器 解決方案. 覺得似乎有些繁瑣...所以產(chǎn)生了寫一篇blog 把這玩意 說透徹的沖動(dòng)... 好啦.我們進(jìn)入正題.
對(duì)于 mouseover 和mouseenter 兩個(gè)事件 最大的區(qū)別就是 mouseenter 是 不冒泡的事件 ..這話怎么理解呢?
<div id=="parent">
<div id="child"></div>
</div>
對(duì)于mouseover 時(shí)間來說 當(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)聽. 則可能會(huì)產(chǎn)生一個(gè)什么問題呢? 從 parent移動(dòng)到child 同樣出發(fā)parent的mouseover 有時(shí)候我們不希望這樣的事情發(fā)生. 這時(shí)候 如果注冊(cè)的監(jiān)聽 是mouseenter的話 無論鼠標(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 同樣無此問題.
知道了區(qū)別 剩下的事情就好辦多了. 遇到此類需求 我們一律mouseenter mouseleave就好..問題是 這玩意只有ie支持 怎么辦呢?
我們只能 用mouseover 和mouseout來模擬 但是如果我們的模擬方案 太過復(fù)雜 那是在就意義不大了... 這時(shí)候我們就可以 借助 xml 方法compareDocumentPosition 來徹底解決這個(gè)問題
我在我的類庫中 封裝了一個(gè)方法 專門用來判斷 某個(gè)節(jié)點(diǎn)的位置 是否在另一個(gè)節(jié)點(diǎn)的子節(jié)點(diǎn)中...
ie可以用 parentNode.contains(childNode) 來判斷 這沒什么好說的 childNode在parentNode DOM樹中存在 那么就是true
而contains方法 ie專屬 那么 我們就是借助 !!(node.compareDocumentPosition(node2) &16) 來實(shí)現(xiàn)同樣的效果.
那么接下來 我們就來談?wù)?compareDocumentPosition 方法 否則 你看到上面的 &16 一定會(huì)困惑無比...
compareDocumentPosition 方法在非ie瀏覽器 都被實(shí)現(xiàn)到 節(jié)點(diǎn)對(duì)象的 中了 所以
node.compareDocumentPosition(node2) 的作用就是 比較 node節(jié)點(diǎn)與node2節(jié)點(diǎn)之間的位置關(guān)系..
他的返回值是一個(gè)number值...
一般來說 對(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 是怎么來的呢? 我們接著往下 看...
試試上 這個(gè)2進(jìn)制算法 是專門用來解釋 兩個(gè)節(jié)點(diǎn)之間的關(guān)系的
這個(gè) 6位2進(jìn)制數(shù) 才是根本所在
第6位 代表 兩個(gè)節(jié)點(diǎn)是否一個(gè)在DOM樹上一個(gè)不在 這個(gè)是針對(duì)整個(gè)DOM樹而言的.也就是說 如果兩個(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 說明 兩個(gè)節(jié)點(diǎn) 要么同時(shí)在DOM樹上 要么同時(shí)不在. 且 兩個(gè)節(jié)點(diǎn) 沒有任何關(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類型 我們就可以判斷出 node是否是 node2的父節(jié)點(diǎn)了...
所以 我們也可以使用 node2.compareDocumentPosition(node) && 8 來判斷node2 是不是 node的子節(jié)點(diǎn) 道理是同樣的
或者我們也可以直接 node.compareDocumentPosition(node2) ==20 來做判斷 這樣還可以省略 !! 做轉(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è) 類似邏輯 來管理 flag標(biāo)識(shí)的類 來說明這個(gè)問題 代碼如下
// 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)識(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 如果沒有則添加
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)如果有 跳過.
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則 說明當(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 來模擬 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);
};
}
大功告成!!!!!
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注