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


Posted in Javascript onMay 04, 2010

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

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

<div id=="parent">
<div id="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 同样无此问题.

 

知道了区别 剩下的事情就好办多了. 遇到此类需求 我们一律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 < len; i++) this._flags[sFlags[i]] = 1 << 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 < len; 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 < len; 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 < len; 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 < len; 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结构:

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

大功告成!!!!!

Javascript 相关文章推荐
Javascript 中文字符串处理额外注意事项
Nov 15 Javascript
js 模拟实现类似c#下的hashtable的简单功能代码
Jan 24 Javascript
详解jquery uploadify 上传文件
Nov 09 Javascript
JavaScript实现url地址自动检测并添加URL链接示例代码
Nov 12 Javascript
JavaScript图片放大技术(放大镜)实现代码分享
Nov 14 Javascript
基于js实现二级下拉联动
Dec 17 Javascript
easyui datebox 时间限制,datebox开始时间限制结束时间,datebox截止日期比起始日期大的实现代码
Jan 12 Javascript
详解如何将 Vue-cli 改造成支持多页面的 history 模式
Nov 20 Javascript
详解Angular5 路由传参的3种方法
Apr 28 Javascript
js实现动态添加上传文件页面
Oct 22 Javascript
记一次Vue.js混入mixin的使用(分权限管理页面)
Apr 17 Javascript
vue组件库的在线主题编辑器的实现思路
Apr 03 Javascript
div+css布局的图片连续滚动js实现代码
May 04 #Javascript
javascript面向对象之Javascript 继承
May 04 #Javascript
Javascript 面向对象之重载
May 04 #Javascript
JavaScript 面向对象的之私有成员和公开成员
May 04 #Javascript
javascript 面向对象的JavaScript类
May 04 #Javascript
JavaScript 面向对象之命名空间
May 04 #Javascript
10个实用的脚本代码工具
May 04 #Javascript
You might like
php.ini 中文版
2006/10/28 PHP
PHP XML操作的各种方法解析(比较详细)
2010/06/17 PHP
用PHP实现的四则运算表达式计算实现代码
2011/08/02 PHP
基于PHP导出Excel的小经验 完美解决乱码问题
2013/06/10 PHP
使用PHP和HTML5 FormData实现无刷新文件上传教程
2014/09/06 PHP
THINKPHP内容分页代码分享
2015/01/14 PHP
Smarty日期时间操作方法示例
2016/11/15 PHP
PHP封装类似thinkphp连贯操作数据库Db类与简单应用示例
2019/05/08 PHP
批量修改标签css样式以input标签为例
2014/07/31 Javascript
javaScript嗅探执行神器-sniffer.js
2017/02/14 Javascript
jQuery使用EasyUi实现三级联动下拉框效果
2017/03/08 Javascript
基于Vue过渡状态实例讲解
2017/09/14 Javascript
详解使用Visual Studio Code对Node.js进行断点调试
2017/09/14 Javascript
基于Vue CSR的微前端实现方案实践
2020/05/27 Javascript
JavaScript文档加载模式以及元素获取
2020/07/28 Javascript
JS访问对象两种方式区别解析
2020/08/29 Javascript
JS实现购物车基本功能
2020/11/08 Javascript
Python中为feedparser设置超时时间避免堵塞
2014/09/28 Python
Python3通过Luhn算法快速验证信用卡卡号的方法
2015/05/14 Python
pandas DataFrame 根据多列的值做判断,生成新的列值实例
2018/05/18 Python
python cv2读取rtsp实时码流按时生成连续视频文件方式
2019/12/25 Python
浅谈在django中使用redirect重定向数据传输的问题
2020/03/13 Python
浅析python字符串前加r、f、u、l 的区别
2021/01/24 Python
Linux常见面试题
2016/10/04 面试题
特色冷饮店创业计划书
2014/01/28 职场文书
淘宝活动策划方案
2014/02/06 职场文书
入党思想汇报怎么写
2014/04/03 职场文书
《桂花雨》教学反思
2014/04/12 职场文书
企业文化理念标语
2014/06/10 职场文书
2014县委书记四风对照检查材料思想汇报
2014/09/21 职场文书
党的群众路线教育实践活动党员个人剖析材料
2014/10/08 职场文书
四风问题查摆剖析材料
2014/10/11 职场文书
会计电算化实训报告
2014/11/04 职场文书
后进生评语大全
2015/01/04 职场文书
升职感谢信
2015/01/22 职场文书
2019最新企业员工考勤管理制度(通用版)!
2019/07/02 职场文书