jQuery的delegate

在网页开发的过程中经常遇到的一个需求就是点击一div内部做某些操作,而点击页面其它地方隐藏该div。比如很多导航菜单,当菜单展开的时候,就会要求点击页面其它非菜单地方,隐藏该菜单。

先从最简单的开始,假如页面有一个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>

在触发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

jQuery的delegate的更多相关文章

  1. 关于Jquery的delegate绑定事件无效

    今天在做一个页面,用的是easyui页面有很多的tabs,里面都放了iframe 需要在load事件动态调整iframe高度 发现始终无法使用delegate来绑定load事件. 纠结了一下午发现了问 ...

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

    在网页开发的过程中经常遇到的一个需求就是点击一div内部做某些操作,而点击页面其它地方隐藏该div.比如很多导航菜单,当菜单展开的时候,就会要求点击页面其它非菜单地方,隐藏该菜单. 先从最简单的开始, ...

  3. jQuery的delegate()与proxy()方法

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

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

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

  5. jQuery的 delegate问题

    习惯了bind,用惯了live,就不习惯delegate了呀有木有... 支持为动态生成的标签元素绑定事件也许就live和delegate了吧,不过新版本已经不支持live了,只有delegate d ...

  6. jQuery中delegate与on的用法与区别

    在jQuery1.7中 .delegate()已被.on()取代.对于早期版本,它仍然使用事件委托的最有效手段. 在事件绑定和委派,delegate()和on在一般情况下,这两种方法是等效的. .de ...

  7. jQuery 事件 - delegate() 方法

    <html><head><script type="text/javascript" src="/jquery/jquery.js" ...

  8. jquery .live() .delegate() .bind() .click()区别

    什么是.live()? 除了让你对Dom元素现在和将来绑定事件之外,.live() 方法和.bind()方法很像.你可以用.live()方法对没有存在的Dom节点绑定事件.考虑下面的情况. 当用户要离 ...

  9. javascript 原生实现 jquery live/delegate

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xht ...

随机推荐

  1. Java设计模式偷跑系列(21)建模和实现享受metapatterns

    转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/40021651 享元模式(Flyweight):运用共享的技术有效地支持大量细粒度的对象. ...

  2. Swift入门教程:基本语法(四)

    可选类型 可选类型的使用场合 当一个值可能存在,可能不存在的时候,就用可选类型 比如查找字符k在某个字符串中的位置 如果字符串是"jake",说明k的位置存在,是2 如果字符串是& ...

  3. CreateMutex

    C++ API CreateMutex 找出当前系统是否已经存在指定进程的实例.假设没有则创建一个相互排斥体.CreateMutex()函数可用来创建一个有名或无名的相互排斥量对象. HANDLE C ...

  4. EasyMonkeyDevice vs MonkeyDevice&HierarchyViewer API Mapping Matrix

    1. 前言 本来这次文章的title是写成和前几篇类似的<EasyMonkeyDevice API实践全记录>,内容也打算把每个API的实践和建议给记录下来,但后来想了下觉得这样子并不是最 ...

  5. ExtJS4 便捷三层开发模式

    ExtJS4 便捷三层开发模式 定义类已经不是ext4.x一个新特性,但与ext3.x的自定义类有语法上的区别.将相关模块封装成类可以有效的减少浏览器的压力,提高渲染速度,同时抽象每一个可重用方法,减 ...

  6. Fizzler

    Fizzler 抓取网站数据不再是难事了,Fizzler(So Easy)全能搞定 首先从标题说起,为啥说抓取网站数据不再难(其实抓取网站数据有一定难度),SO EASY!!!使用Fizzler全搞定 ...

  7. Ubuntu10.04中间Leach协议一键安装

    半天后,尝试,引用网络上的零散资源,成品博客Leach协议ubuntu10.04在安装(12.04也可以在右侧安装,然而,实施效果的不,求解决~~),并制作了补丁. 一个关键的安装步骤如下面: 1.在 ...

  8. Web开发的发展

    领导以前是做C的,没有做过Web开发,就问我,Web技术发展的大致过程,我就是简单的说了开发过程的演化,下来后有自己找些资料补充下,如下所示:(着这是个简单的说明,感兴趣的可以再自己找找资料). 1. ...

  9. POJ2533 Longest Ordered Subsequence 【最长递增子序列】

    Longest Ordered Subsequence Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 32192   Acc ...

  10. Entity Framework 丢失数据链接的绑定,在已绑好的EDMX中提示“Choose Your Data Connection”

    早先做的一个练手的项目中, 使用到了Entity framework . 最近碰到一个问题,在edmx 里面选择“Update model from Database” 的时候提示了 “Choose ...