由点击页面其它地方隐藏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 相关文章推荐
js parentElement和offsetParent之间的区别
Mar 23 Javascript
在Windows上安装Node.js模块的方法
Sep 25 Javascript
jquery的相对父元素和相对文档定位示例代码
Aug 02 Javascript
jQuery插件ImageDrawer.js实现动态绘制图片动画(附源码下载)
Feb 25 Javascript
JavaScript6 let 新语法优势介绍
Jul 15 Javascript
javascript数组常用方法汇总
Sep 10 Javascript
微信小程序 实战程序简易新闻的制作
Jan 09 Javascript
详解使用fetch发送post请求时的参数处理
Apr 05 Javascript
对angularJs中ng-style动态改变样式的实例讲解
Sep 30 Javascript
对Layer弹窗使用及返回数据接收的实例详解
Sep 26 Javascript
jQuery事件模型默认行为执行顺序及trigger()与 triggerHandler()比较实例分析
Apr 30 jQuery
CocosCreator如何实现划过的位置显示纹理
Apr 14 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方法调用模式与函数调用模式简例
2011/09/20 PHP
ThinkPHP实现非标准名称数据表快速创建模型的方法
2014/11/29 PHP
PHP全局变量与超级全局变量区别分析
2016/04/01 PHP
js基于qrcode.js生成二维码的方法【附demo插件源码下载】
2016/12/28 PHP
老生常谈PHP中的数据结构:DS扩展
2017/07/17 PHP
yii2 开发api接口时优雅的处理全局异常的方法
2019/05/14 PHP
PHP实现时间日期友好显示实现代码
2019/09/08 PHP
JAVASCRIPT  THIS详解 面向对象
2009/03/25 Javascript
jQuery select控制插件
2009/08/17 Javascript
JavaScript显示表单内元素数量的方法
2015/04/02 Javascript
jQuery实现复选框批量选择与反选的方法
2015/06/17 Javascript
JavaScript+html5 canvas绘制的小人效果
2016/01/27 Javascript
扩展jquery easyui tree的搜索树节点方法(推荐)
2016/10/28 Javascript
微信小程序 wxapp视图容器 view详解
2016/10/31 Javascript
js异步编程小技巧详解
2017/08/14 Javascript
Vue实现动态添加或者删除对象和对象数组的操作方法
2018/09/21 Javascript
微信小程序实现人脸识别登陆的示例代码
2019/04/02 Javascript
更强大的vue ssr实现预取数据的方式
2019/07/19 Javascript
nginx部署多个vue项目的方法示例
2020/09/06 Javascript
基于python3 类的属性、方法、封装、继承实例讲解
2017/09/19 Python
python中pylint使用方法(pylint代码检查)
2018/04/06 Python
解决pycharm remote deployment 配置的问题
2019/06/27 Python
VPS CENTOS 上配置python,mysql,nginx,uwsgi,django的方法详解
2019/07/01 Python
给你一面国旗 教你用python画中国国旗
2019/09/24 Python
Python序列类型的打包和解包实例
2019/12/21 Python
带你学习Python如何实现回归树模型
2020/07/16 Python
python归并排序算法过程实例讲解
2020/11/04 Python
html5实现移动端适配完美写法
2017/11/16 HTML / CSS
params有什么用
2016/03/01 面试题
什么是ESB?请介绍一下ESB?
2015/05/27 面试题
大学军训感言200字
2014/02/26 职场文书
2014年教研活动总结范文
2014/04/26 职场文书
灰雀教学反思
2014/04/28 职场文书
经销商年会策划方案
2014/05/29 职场文书
小学语文教师研修感悟
2015/11/18 职场文书
GO语言字符串处理函数之处理Strings包
2022/04/14 Golang