热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

跨浏览器的mouseentermouseleave以及compareDocumentPosition的使用说明_javascript技巧

昨天去大牛司徒正美的blog看博文突然看到关于onmouseenter和onmouseleave两个ie专有事件..
写了这么久 js应用 我居然不知道这两个事件 于是 去google搜索了一番. 才发现这两个事件 是如此的优秀 且好用... 但搜索过程中 发现 好多人 似乎不太明白这两个事件 和mouseover mouseout 真正的区别 和用途.. 并且看到google中搜索得到的 一些朋友 实现的 跨浏览器 解决方案. 觉得似乎有些繁琐...所以产生了写一篇blog 把这玩意 说透彻的冲动... 好啦.我们进入正题.

对于 mouseover 和mouseenter 两个事件 最大的区别就是 mouseenter 是 不冒泡的事件 ..这话怎么理解呢?



对于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 同样无此问题.

知道了区别 剩下的事情就好办多了. 遇到此类需求 我们一律mouseenter mouseleave就好..问题是 这玩意只有ie支持 怎么办呢?

我们只能 用mouseover 和mouseout来模拟 但是如果我们的模拟方案 太过复杂 那是在就意义不大了... 这时候我们就可以 借助 xml 方法compareDocumentPosition 来彻底解决这个问题

我在我的类库中 封装了一个方法 专门用来判断 某个节点的位置 是否在另一个节点的子节点中...

ie可以用 parentNode.contains(childNode) 来判断 这没什么好说的 childNode在parentNode DOM树中存在 那么就是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***的数...

那么这些 20 10 4 2 0 是怎么来的呢? 我们接着往下 看...

试试上 这个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的后面)

最后 如果 2 3 4 5 6 位 都为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是一样的...

我用js 实现了一个 类似逻辑 来管理 flag标识的类 来说明这个问题 代码如下

// flag 类
function flag(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 (var i = 0, len = sFlags.length; i

//这里初始化标识的值 如果同时具备n种状态 则 每种状态的值一定是 000001 000010 000100 001000 010000 100000
},
setStatus: function(sFlags) { //sFlags "状态1,状态3......设置当前状态
sFlags = sFlags.split(',');

    this._status=0;
for (var i = 0, len = sFlags.length; i this._check[sFlags[i]];
this._status |= this._flags[sFlags[i]];
}
},
addStatus: function(sFlags) {//sFlags "状态1,状态3......检查当前状态标示 是否有 状态1和状态3 如果没有则添加
sFlags = sFlags.split(',');
for (var i = 0, len = sFlags.length; i this._check(sFlags[i]);
if (this.hasStatus(sFlags[i])) continue; //判断是否已经有这个状态如果有 跳过.
this._status |= this._flags[sFlags[i]];

//当前的状态值 与 允许的标识值 做 | 运算 即添加状态
}
},
removeStatus: function(sFlags) {
sFlags = sFlags.split(',');
for (var i = 0, len = sFlags.length; i this._check(sFlags[i]);
if (!this.hasStatus(sFlags[i])) continue;
this._status ^= this._flags[sFlags[i]];

// 当前状态值 与 要去掉的状态值做 ^运算 即删除状态
}
},
clear: function() {
this._status = 0;
},
hasStatus: function(sFlags) {//sFlags "状态1,状态3.....状态 n.检查当前状态标识 是否同时 具备状态1和状态3 以及状态n
sFlags = sFlags.split(',');
for (var i = 0, len = sFlags.length; i this._check(sFlags[i]);
if ((this._status & this._flags[sFlags[i]]) != this._flags[sFlags[i]]) return false;

//当前状态值 与输入的状态做 & 运算 如果返回值 不等于 改状态 的标识值 则 return false .

//比如 010101 & 010000 返回010000则 说明当前标识值具备 010000这个状态.

}
return true;
},
_check: function(sFlag) {
if (!sFlag in this._flags) throw new Error(" 当前 flag 中不存在" + sFlag + "标识");

//检查当前输入状态字符串 是否是合法值.
}
}

用法:

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结构:










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);
};
}

大功告成!!!!!

推荐阅读
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 学习SLAM的女生,很酷
    本文介绍了学习SLAM的女生的故事,她们选择SLAM作为研究方向,面临各种学习挑战,但坚持不懈,最终获得成功。文章鼓励未来想走科研道路的女生勇敢追求自己的梦想,同时提到了一位正在英国攻读硕士学位的女生与SLAM结缘的经历。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • “你永远都不知道明天和‘公司的意外’哪个先来。”疫情期间,这是我们最战战兢兢的心情。但是显然,有些人体会不了。这份行业数据,让笔者“柠檬” ... [详细]
  • 生成对抗式网络GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN介绍
    一、GAN原理介绍学习GAN的第一篇论文当然由是IanGoodfellow于2014年发表的GenerativeAdversarialNetworks(论文下载链接arxiv:[h ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • qt学习(六)数据库注册用户的实现方法
    本文介绍了在qt学习中实现数据库注册用户的方法,包括登录按钮按下后出现注册页面、账号可用性判断、密码格式判断、邮箱格式判断等步骤。具体实现过程包括UI设计、数据库的创建和各个模块调用数据内容。 ... [详细]
  • 无线认证设置故障排除方法及注意事项
    本文介绍了解决无线认证设置故障的方法和注意事项,包括检查无线路由器工作状态、关闭手机休眠状态下的网络设置、重启路由器、更改认证类型、恢复出厂设置和手机网络设置等。通过这些方法,可以解决无线认证设置可能出现的问题,确保无线网络正常连接和上网。同时,还提供了一些注意事项,以便用户在进行无线认证设置时能够正确操作。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
author-avatar
Adam_phper
与时俱进
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有