跨浏览器的mouseentermouseleave以及compareDocumentPosition的使用说明
写了这么久js应用我居然不知道这两个事件于是去google搜索了一番.才发现这两个事件是如此的优秀且好用...但搜索过程中发现好多人似乎不太明白这两个事件和mouseovermouseout真正的区别和用途.. 并且看到google中搜索得到的 一些朋友实现的跨浏览器解决方案.觉得似乎有些繁琐...所以产生了写一篇blog把这玩意说透彻的冲动...好啦.我们进入正题.
对于mouseover和mouseenter两个事件最大的区别就是mouseenter 是不冒泡的事件..这话怎么理解呢?
<divid=="parent">
<divid="child"></div>
</div>
对于mouseover时间来说当鼠标从其他元素移动到child节点时发生但此事件会冒泡所以会导致parent也出发mouseover
如果我们对parent注册了mouseover监听.则可能会产生一个什么问题呢?从parent移动到child同样出发parent的mouseover 有时候我们不希望这样的事情发生.这时候如果注册的监听是mouseenter的话 无论鼠标从任何元素移动到child时只有child元素发生mouseenter事件而其祖宗节点都不会因为冒泡而触发此事件...这就使我们可以彻底放弃 我们以往为了实现同样的逻辑 又要对子节点禁止mouseover冒泡或者又去判断事件源对象或判断srcElement/relatedTarget那样麻烦的方案.
对于mouseout和mouseleave也是如此当鼠标从child移出时mouseout同样会冒泡到parent从而触发parent的mouseout二mouseleave同样无此问题.
知道了区别剩下的事情就好办多了.遇到此类需求我们一律mouseentermouseleave就好..问题是这玩意只有ie支持怎么办呢?
我们只能用mouseover和mouseout来模拟 但是如果我们的模拟方案太过复杂那是在就意义不大了...这时候我们就可以借助xml方法compareDocumentPosition来彻底解决这个问题
我在我的类库中封装了一个方法专门用来判断某个节点的位置是否在另一个节点的子节点中...
ie可以用parentNode.contains(childNode)来判断这没什么好说的childNode在parentNodeDOM树中存在 那么就是true
而contains方法ie专属那么我们就是借助!!(node.compareDocumentPosition(node2)&16)来实现同样的效果.
那么接下来我们就来谈谈compareDocumentPosition方法否则你看到上面的&16一定会困惑无比...
compareDocumentPosition方法在非ie浏览器都被实现到节点对象的中了 所以
node.compareDocumentPosition(node2)的作用就是比较node节点与node2节点之间的位置关系..
他的返回值是一个number值...
一般来说对我们有用的是以下几个值
1. 20 (2进制:010100)
2. 10 (2进制: 001010)
3. 4 (2进制: 000100)
4. 2 (2进制: 000010)
5. 0 (2进制: 000000)
6. 2进100***的数...
那么这些2010420是怎么来的呢?我们接着往下看...
试试上这个2进制算法是专门用来解释两个节点之间的关系的
这个6位2进制数才是根本所在
第6位代表两个节点是否一个在DOM树上一个不在这个是针对整个DOM树而言的.也就是说如果两个都不在或两个都在则为0否则为1
第5位代表node是否是node2的父节点如果是则为1否则为0
第4位代表node是否是node2的子节点如果是则为1否则为0
第3位代表node是否在node2的前面如果是则为1否则为0(注:如果node是node2的父节则node同时也看做在node2的前面)
第2位代表node是否在node2的后面如果是则为1否则为0(注如果node是node2的子节点则node同时也看做在node2的后面)
最后如果23456位都为0即000000说明两个节点要么同时在DOM树上要么同时不在.且两个节点没有任何关系那么只有一种可能即两个节点是同一个节点...也就是node==node2
所以node.compareDocumentPosition(node2)&16位运算的结果是什么呢?以上几种可能的组合中只有010100 &010000 以及110***&16 的结果是010000 即返回16其他情况 均返回0然后用!!吧number隐式转型成boolean类型我们就可以判断出node是否是node2的父节点了...
所以我们也可以使用node2.compareDocumentPosition(node)&&8来判断node2是不是node的子节点道理是同样的
或者我们也可以直接node.compareDocumentPosition(node2)==20来做判断这样还可以省略!!做转型..也是可以的.
到了这里聪明你的一定发现这玩意是什么?分明就是c#中 flag标识枚举的用法...
c#中File.Attributes枚举可能同时具备n多属性比如一个文件可以是只读的同时还可以是隐藏的或者同时还可以是共享的.等等
那么用一个枚举值如何确定一个文件同时具备哪些属性又不产生冲突呢?答案于compareDocumentPosition是一样的...
//flag类
functionflag(sFlags){
this._flags={};
this._status=0;
sFlags&&this.initFlags(sFlags);
}
flag.prototype={
constructor:flag,
initFlags:function(sFlags){//sFlags"状态1,状态2,状态3......初始化原始标识集合...
sFlags=sFlags.split(",");
for(vari=0,len=sFlags.length;i<len;i++)this._flags[sFlags[i]]=1<<i;
},
setStatus:function(sFlags){//sFlags"状态1,状态3......设置当前状态
sFlags=sFlags.split(",");
this._status=0;
for(vari=0,len=sFlags.length;i<len;i++){
this._check[sFlags[i]];
this._status|=this._flags[sFlags[i]];
}
},
addStatus:function(sFlags){//sFlags"状态1,状态3......检查当前状态标示是否有状态1和状态3如果没有则添加
sFlags=sFlags.split(",");
for(vari=0,len=sFlags.length;i<len;i++){
this._check(sFlags[i]);
if(this.hasStatus(sFlags[i]))continue;
}
},
removeStatus:function(sFlags){
sFlags=sFlags.split(",");
for(vari=0,len=sFlags.length;i<len;i++){
this._check(sFlags[i]);
if(!this.hasStatus(sFlags[i]))continue;
}
},
clear:function(){
this._status=0;
},
hasStatus:function(sFlags){//sFlags"状态1,状态3.....状态n.检查当前状态标识是否同时具备状态1和状态3以及状态n
sFlags=sFlags.split(",");
for(vari=0,len=sFlags.length;i<len;i++){
this._check(sFlags[i]);
if((this._status&this._flags[sFlags[i]])!=this._flags[sFlags[i]])returnfalse;
}
returntrue;
},
_check:function(sFlag){
if(!sFlaginthis._flags)thrownewError("当前flag中不存在"+sFlag+"标识");
}
}
用法:
varfileStatus=newflag("readOnly,hidden,otherStatus");
fileStatus.setStatus("readOnly,hidden");
alert(fileStatus.hasStatus("readOnly"))//true;
alert(fileStatus.hasStatus("hidden"))//true;
alert(fileStatus.hasStatus("otherStatus"))//false;
最后我们回到正题我们借助compareDocumentPosition来模拟mouseentermouseleave
DOM结构:
<divid="dd"style="background-color:#369;width:50%;height:50%;position:absolute;left:25%;top:25%;">
<divstyle="background-color:#ff0;width:50%;height:50%;position:relative;left:25%;top:25%">
<divstyle="background-color:#789;width:50%;height:50%;position:relative;left:25%;top:25%">
<divstyle="background-color:#123;width:50%;height:50%;position:relative;left:25%;top:25%">
<divstyle="background-color:#456;width:50%;height:50%;position:relative;left:25%;top:25%">
</div>
</div>
</div>
</div>
</div>
js脚本:
vardd=document.getElementById("dd")
if(!+"\v1"){//ie
dd.onmouseenter=function(){alert(1);};
dd.onmouseleave=function(){alert(2);};
}
else{//others
dd.onmouseover=function(e){
vart=e.relatedTarget;
vart2=e.target;
this==t2&&t&&!(t.compareDocumentPosition(this)&8)&&alert(1);
};
dd.onmouseout=function(e){
vart=e.relatedTarget;
vart2=e.target;
this==t2&&t&&!(t.compareDocumentPosition(this)&8)&&alert(2);
};
}
大功告成!!!!!
相关文章
- 解决H5页面在微信浏览器中打开 input file 在安卓上没有拍照选项
- Chrome浏览器内置翻译无法使用,右键翻译无反应?
- selenium最大化浏览器-Selenium启动常用浏览器
- 360浏览器兼容ie6-如何使用360浏览器(360浏览器出问题解决方法)
- idm下载器如何使用 idm下载器使用技巧(电脑版、手机版、浏览器插件)
- And Design5 360安全浏览器样式错乱问题解决
- Chrome谷歌浏览器自带翻译功能无法使用的解决方案
- 使用 viewport meta 标签在手机浏览器上控制布局
- CSS 之 Opacity多浏览器透明度兼容处理详解编程语言
- Linux操作系统无法打开浏览器(linux打不开浏览器)
- 微软这思路有些清奇:Microsoft Edge浏览器竟然集成数学求解器
- 使用Linux自带的浏览器浏览网页(linux自带的浏览器)
- Microsoft Edge 93优化垂直标签页体验:可隐藏浏览器标题栏
- 浏览器轻松连接Redis服务(浏览器连接redis)
- 用PHP的ob_start();控制您的浏览器cache!
- 实现png图片和png背景透明(支持多浏览器)的方法
- JS获取屏幕,浏览器窗口大小,网页高度宽度(实现代码)