0

0

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

php中文网

php中文网

发布时间:2016-05-16 18:28:06

|

1327人浏览过

|

来源于php中文网

原创

写了这么久 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 那样麻烦的方案.

立即学习Java免费学习笔记(深入)”;

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

大功告成!!!!!

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

616

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

194

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

91

2026.02.13

TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

20

2026.02.13

Redis高可用架构与分布式缓存实战
Redis高可用架构与分布式缓存实战

本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。

54

2026.02.13

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

29

2026.02.12

雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

15

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

598

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

56

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号