由点击页面其它地方隐藏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 相关文章推荐
List the UTC Time on a Computer
Jun 11 Javascript
javascript获得网页窗口实际大小的示例代码
Sep 21 Javascript
JSON+HTML实现国家省市联动选择效果
May 18 Javascript
javascript实现下拉提示选择框
Dec 29 Javascript
ligerUI---ListBox(列表框可移动的实例)
Nov 28 Javascript
基于vue展开收起动画的示例代码
Jul 05 Javascript
解决vue-cli单页面手机应用input点击手机端虚拟键盘弹出盖住input问题
Aug 25 Javascript
vue-swiper的使用教程
Aug 30 Javascript
原生JS实现轮播图效果
Oct 12 Javascript
微信小程序实现授权登录
May 15 Javascript
vue+elementUI实现表格关键字筛选高亮
Oct 26 Javascript
基于jsbarcode 生成条形码并将生成的条码保存至本地+源码
Apr 27 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判断GIF图片是否为动画的方法
2020/09/04 PHP
PHP定义字符串的四种方式详解
2018/02/06 PHP
深入理解JavaScript定时机制
2010/10/29 Javascript
深入理解Javascript闭包 新手版
2010/12/28 Javascript
jQuery EasyUI API 中文文档 - NumberBox数字框
2011/10/13 Javascript
Dom 学习总结以及实例的使用介绍
2013/04/24 Javascript
Javascript实现的常用算法(如冒泡、快速、鸽巢、奇偶等)
2014/04/29 Javascript
jquery 取子节点及当前节点属性值
2014/07/25 Javascript
JS获取iframe中longdesc属性的方法
2015/04/01 Javascript
如何根据百度地图计算出两地之间的驾驶距离(两种语言js和C#)
2015/10/29 Javascript
JavaScript中Window对象的属性及事件
2015/12/25 Javascript
在Mac OS上安装使用Node.js的项目自动化构建工具Gulp
2016/06/18 Javascript
Bootstrap超大屏幕的实现代码
2017/03/22 Javascript
微信小程序使用picker实现时间和日期选择框功能【附源码下载】
2017/12/11 Javascript
浅谈Vue中render中的h箭头函数
2019/11/07 Javascript
js中关于Blob对象的介绍与使用
2019/11/29 Javascript
JS实现百度搜索框关键字推荐
2020/02/17 Javascript
vue中实现拖动调整左右两侧div的宽度的示例代码
2020/07/22 Javascript
JavaScript实现弹出窗口效果
2020/12/09 Javascript
Python3中条件控制、循环与函数的简易教程
2017/11/21 Python
简单实现Python爬取网络图片
2018/04/01 Python
Numpy中矩阵matrix读取一列的方法及数组和矩阵的相互转换实例
2018/07/02 Python
uwsgi+nginx部署Django项目操作示例
2018/12/04 Python
python能做什么 python的含义
2019/10/12 Python
python数据预处理方式 :数据降维
2020/02/24 Python
Python pytesseract验证码识别库用法解析
2020/06/29 Python
解决Python 写文件报错TypeError的问题
2020/10/23 Python
HTML5文档结构标签
2017/04/21 HTML / CSS
办护照工作证明范本
2014/01/14 职场文书
护士毕业自我鉴定
2014/02/07 职场文书
书法比赛获奖感言
2014/02/10 职场文书
歌唱比赛主持词
2014/03/18 职场文书
2014年九一八事变演讲稿
2014/09/14 职场文书
2014年居委会工作总结
2014/12/09 职场文书
会议营销主持词
2015/07/03 职场文书
餐厅如何利用“营销策略”扭转亏本局面
2019/10/15 职场文书