由点击页面其它地方隐藏div所想到的jQuery的delegate


Posted in Javascript onAugust 29, 2013

先从最简单的开始,假如页面有一个id为test的div,我们要实现点击页面其它地方隐藏该div:

<div id="test" style="margin:100px;background-color:#3e3;width:100px;height:100px;">        </div>

对于这个问题一般有两种思路,这两种思路都会利用事件冒泡这一原理,想要详细了解Javascript事件机制可以看看JavaScript与HTML交互——事件,这不是本文重点,所以这里只是简单介绍一下事件冒泡,

事件冒泡

IE的事件冒泡:事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的元素

Netscape的事件捕获:不太具体的节点更早接收事件,而最具体的元素最后接收事件,和事件冒泡相反

DOM事件流:DOM2级事件规定事件流包括三个阶段,事件捕获阶段,处于目标阶段,事件冒泡阶段,首先发生的是事件捕获,为截取事件提供机会,然后是实际目标接收事件,最后是冒泡句阶段。

Opera、Firefox、Chrome、Safari都支持DOM事件流,IE不支持事件流,只支持事件冒泡

如有以下html,点击div区域,按照不同的模型事件元素的click事件触发顺序如下所示:

<!DOCTYPE html >
<html>
<head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <title>Test Page</title>
</head>
<body>
    <div>
        Click Here</div>
</body>
</html>

由点击页面其它地方隐藏div所想到的jQuery的delegate

在触发DOM上的某个事件的时候会产生一个事件对象event,这个对象包含着所有与事件有关的信息,包括产生事件的元素、事件类型等相关信息。所有浏览都支持event对象,但支持方式不同。事件对象有一个方法(W3C:stopPropagation)/属性(IE:cancelBulle=true)可以阻止事件继续冒泡或捕获。我们如果想在事件冒泡到某元素时阻止冒泡可以写一个这样的兼容浏览器方法:

function stopPropagation(e) {//把事件对象传入
            if (e.stopPropagation) //支持W3C标准
                e.stopPropagation();
            else //IE8及以下浏览器
                e.cancelBubble = true;
        }

因为所有的浏览器都支持事件冒泡,浏览器兼容性考虑,我们一般绑定事件的的时候都会利用事件冒泡而不是事件捕获。了解了这个之后我们可以看看下面两种思路了。

思路一
第一种思路分两步

第一步:对document的click事件绑定事件处理程序,使其隐藏该div

第二步:对div的click事件绑定事件处理程序,阻止事件冒泡,防止其冒泡到document,而调用document的onclick方法隐藏了该div。

<script type="text/javascript">
            function stopPropagation(e) {
                if (e.stopPropagation) 
                    e.stopPropagation();
                else 
                    e.cancelBubble = true;
            }
            $(document).bind('click',function(){
                $('#test').css('display','none');
            });
            $('#test').bind('click',function(e){
                stopPropagation(e);
            });
        </script>

这样当点击页面非div区域的时候,直接或层层冒泡会调用document的onclick方法,隐藏该div,而点击div或其子元素的时候,事件总会冒泡的div本身,这时候会阻止事件继续冒泡,不会调用doument的onclick方法致使div被隐藏,从而完成了我们的需求。

思路二

我们之前提到,在触发DOM上的某个事件的时候会产生一个事件对象event,这个对象包含着所有与事件有关的信息,包括产生事件的元素、事件类型等相关信息,思路一中div的click事件处理程序传入的参数就是这个event对象。访问IE中的event对象有几种不同的方式,取决于指定事件处理程序的方法。直接为DOM元素添加事件处理程序时,event对象作为window对象的一个属性存在。

event对象包含了一个重要属性:target(W3C)/srcElement(IE),这个属性标识了触发事件的原始元素,思路二就是要利用这个属性。我们可以直接对document的click事件绑定事件处理程序,在事件处理程序中判读事件源是否为id==test的div元素或其子元素,如果是则方法return不做操作,如果不是则隐藏该div。

<script type="text/javascript">
            $(document).bind('click',function(e){
                var e = e || window.event; //浏览器兼容性
                var elem = e.target || e.srcElement;
                while (elem) { //循环判断至跟节点,防止点击的是div子元素
                    if (elem.id && elem.id=='test') {
                        return;
                    }
                    elem = elem.parentNode;
                }
                $('#test').css('display','none'); //点击的不是div或其子元素
            });
        </script>

这样当点击页面任何地方的时候都会层层冒泡至document的click事件,事件处理程序会判断事件源是否为id==test的div或其子元素,如果是方法return,否则隐藏该div,也能够实现我们的需求。

注意点及优劣

这两种思路都依赖于事件冒泡,所以我们在处理其它相关元素的click事件的时候一定要注意这点,避免其他相关元素的click事件处理程序中包含阻止事件冒泡代码而影响了该功能。

这两种方式都很容易理解,貌似思路一更优秀一些,看起来它的处理更简单一些,不用去层层判断事件源,直接把click事件绑定在该div上。在这个例子中确实如此,但是有些复杂的页面就不尽然了,假如我们有一个页面,上面有数十个div都需要点击页面其它地方隐藏这类问题

<div class="dialogs">
        <div class="dialog">
            <div id="1">1</div>
            <div id="2">2</div>
        </div>
        <div class="dialog">
            <div id="1">1</div>
            <div id="2">2</div>
        </div>
        <div class="dialog">
            <div id="1">1</div>
            <div id="2">2</div>
        </div>
        ...
    </div>

我们用思路一写出的代码可能是这样:

<script type="text/javascript">
            function stopPropagation(e) {
                if (e.stopPropagation) 
                    e.stopPropagation();
                else 
                    e.cancelBubble = true;
            }
            $(document).bind('click',function(){
                $('.dialog').css('display','none');
            });
            $('.dialog').bind('click',function(e){
                stopPropagation(e);
            });
        </script>

看起来简单依旧的样子,但是我们仔细想想就会发现问题,我们在每个dialog上都绑定了类似的方法,维护如此多的click事件处理程序对内存来说绝对是可开销,导致我们页面运行缓慢。而且如果我们可以动态使用ajax创建新dialog问题又来了,新创建的dialog不能实现隐藏功能!因为绑定函数已经执行完了,不会再为新的dialog绑定click事件处理程序,我们只能自己来做此事。也就是说思路一无法把处理程序附加到可能还未存在于DOM中的DOM元素之上。因为它是直接把处理程序绑定到各个元素上,它不能把处理程序绑定到还未存在于页面中的元素之上。

这时候就是思路二展示身手的时候了,我们看看思路二在这种时候代码的书写

<script type="text/javascript">
            $(document).bind('click',function(e){
                var e = e || window.event;
                var elem = e.target || e.srcElement;
                while (elem) {
                    if (elem.className && elem.className.indexOf('dialog')>-1) {
                        return;
                    }
                    elem = elem.parentNode;
                }
                $('#test').css('display','none'); 
            });
        </script>

改动也相当的小,我们来看看是不是能解决上边的两个问题了,首先无论多少个dialog我们只是绑定了一个click事件处理程序,对性能影响不大,添加一个新的dialog思路二的代码还好不好使呢,依旧好使,这样我们就能发现在复杂页面的情况下实际上思路二是一种更优秀的解决方案。

这些都明白了,我们就能说说本文的第二个主角jQuery的delegate方法了。

delegate
首先看看jQuery官方对delegate的语法及描述

.delegate( selector, eventType, handler(eventObject) )

Description: Attach a handler to one or more events for all elements that match the selector, now or in the future, based on a specific set of root elements.

delegate() 方法为指定的元素(属于被选元素的子元素)添加一个或多个事件处理程序,并规定当这些事件发生时运行的函数。

使用 delegate() 方法的事件处理程序适用于当前或未来的元素(比如由脚本创建的新元素)。

$( "table" ).delegate( "td", "click", function() {
      $( this ).toggleClass( "chosen" );
    });

    通过上面语句我们就可以为所有table的td绑定click事件处理程序。

delegate方法设计意图在于把处理程序附加到单个元素上或是一小组元素之上,监听后代元素上的事件而不是循环遍历并把同一个函数逐个附加到DOM中的多个个元素上。把处理程序附加到一个(或是一小组)祖先元素上而不是直接把处理程序附加到页面中的所有元素上,从而带来性能上的优化。

jQuery版隐藏dialog

通过上面知识我们可以发现jQuery的delegate方法可以方便实现我们隐藏div的需求

<script type="text/javascript">
            $('.dialogs').delegate('.dialog','click',function(){
                $(this).css('display','none');
            });
        </script>

 使用jQuery我们发现比我们思路二在性能上又有了小幅提升,因为我们不需要冒泡至document处理了,只需要在dialog的父元素就可以处理完成了,可以不至于把很多类似功能都绑定到document上,需要注意的一点就是jQuery已经贴心的帮我们把this处理为事件源,处理起来更是如鱼得水了。

delegate与bind
通过上面我们说一堆我们可以在权衡使用bind还是delegate上有一定依据了,如果就单独绑定一个元素的事件处理程序,用bind还是很合适的,但是如果处理很多类似元素的事件处理程序的时候不妨考虑一下delegate,看看是否对提高性能有所帮助。

Javascript 相关文章推荐
用javascript实现的激活输入框后隐藏初始内容
Jun 29 Javascript
一个用javascript写的select支持上下键、首字母筛选以及回车取值的功能
Sep 09 Javascript
jQuery+CSS实现菜单滑动伸展收缩(仿淘宝)
Mar 22 Javascript
js快速排序的实现代码
Dec 08 Javascript
JavaScript设计模式之工厂方法模式介绍
Dec 28 Javascript
javascript实现的图片切割多块效果实例
May 07 Javascript
基于javascript实现浏览器滚动条快到底部时自动加载数据
Nov 30 Javascript
js console.log打印对像与数组用法详解
Jan 21 Javascript
Angular2利用组件与指令实现图片轮播组件
Mar 27 Javascript
Vue.js 插件开发详解
Mar 29 Javascript
Bootstrap实现省市区三级联动(亲测可用)
Jul 26 Javascript
在Koa.js中实现文件上传的接口功能
Oct 08 Javascript
JavaScript自定义事件介绍
Aug 29 #Javascript
JavaScript包装对象使用介绍
Aug 29 #Javascript
JavaScript作用域链使用介绍
Aug 29 #Javascript
JavaScript 命名空间 使用介绍
Aug 29 #Javascript
JavaScript prototype 使用介绍
Aug 29 #Javascript
JavaScript创建对象的写法
Aug 29 #Javascript
jQuery实现用户注册的表单验证示例
Aug 28 #Javascript
You might like
php读取html并截取字符串的简单代码
2009/11/30 PHP
ThinkPHP的cookie和session冲突造成Cookie不能使用的解决方法
2014/07/01 PHP
php去除头尾空格的2种方法
2015/03/16 PHP
层序遍历在ExtJs的TreePanel中的应用
2009/10/16 Javascript
一些javascript一些题目的解析
2010/12/25 Javascript
node.js操作mongodb学习小结
2015/04/25 Javascript
JS实现的在线调色板实例(附demo源码下载)
2016/03/01 Javascript
Bootstrap风格的zTree右键菜单
2017/02/17 Javascript
ES6中Proxy与Reflect实现重载(overload)的方法
2017/03/30 Javascript
jstree单选功能的实现方法
2017/06/07 Javascript
vue中element组件样式修改无效的解决方法
2018/02/03 Javascript
Vue兼容ie9的问题全面解决方案
2018/06/19 Javascript
babel7.x和webpack4.x配置vue项目的方法步骤
2019/05/12 Javascript
react结合bootstrap实现评论功能
2020/05/30 Javascript
JavaScript冒泡算法原理与实现方法深入理解
2020/06/04 Javascript
js判断鼠标移入移出方向的方法
2020/06/24 Javascript
微信小程序反编译的实现
2020/12/10 Javascript
python3 打开外部程序及关闭的示例
2018/11/06 Python
Python脚本修改阿里云的访问控制列表的方法
2019/03/08 Python
基于Python中isfile函数和isdir函数使用详解
2019/11/29 Python
python使用协程实现并发操作的方法详解
2019/12/27 Python
Python Opencv中用compareHist函数进行直方图比较对比图片
2020/04/07 Python
鲜为人知的HTML5语音合成功能
2019/05/17 HTML / CSS
不同浏览器创建XMLHttpRequest方法有什么不同
2014/11/17 面试题
环保倡议书
2014/04/14 职场文书
服装发布会策划方案
2014/05/22 职场文书
电子信息工程专业求职信
2014/06/28 职场文书
2014年小学教师工作自我评价
2014/09/22 职场文书
2014年财政工作总结
2014/12/10 职场文书
2015年党务公开工作总结
2015/05/19 职场文书
同学聚会致辞集锦
2015/07/28 职场文书
小学生反邪教心得体会
2016/01/15 职场文书
高中语文教学反思范文
2016/02/16 职场文书
2017新年晚会开幕词
2016/03/03 职场文书
涨工资申请书应该怎么写?
2019/07/08 职场文书
话题作文之呼唤
2019/12/18 职场文书