事件处理器:onclick、onmouseover....

在传统的事件处理中,你需要为每一个元素添加或者是删除事件处理器。然而,事件处理器将有可能导致内存泄露或者是性能下降——你用得越多这种风险就越大。

JavaScript事件代理:当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委托给父节点来触发处理函数。这主要得益于浏览器的事件冒泡机制

它是怎么运作的呢?

事件代理用到了两个特性:事件冒泡以及目标元素

事件冒泡:当一个元素上的事件被触发的时候,比如说鼠标点击了一个按钮,同样的事件将会在那个元素的所有祖先元素中被触发。这一过程被称为事件冒泡;这个事件从原始元素开始一直冒泡到DOM树的最上层。

目标元素:任何一个事件的目标元素都是最开始的那个元素,也就是我们出发的元素。在我们的这个例子中也就是按钮。

使用事件代理:把事件处理器添加到一个元素上,等待一个事件从它的子级元素里冒泡上来,并且可以得知这个事件是从哪个元素开始的。

用代码写出来是什么样呢?

我们所要关心的只是如何检测目标元素而已。比方说我们有一个table元素,ID是“report”,我们为这个表格添加一个事件处理器以调用editCell函数。editCell函数需要判断传到table来的事件的目标元素。考虑到我们要写的几个函数中都有可能用到这一功能,所以我们把获取目标元素单独放到一个名为getEventTarget的函数中:

function getEventTarget(e) {
//event属性兼容写法
e = e || window.event;
//获取目标元素兼容写法
return e.target || e.srcElement;
}

接下来就是editCell函数了,这个函数调用到了getEventTarget函数。一旦我们得到了目标元素,剩下的事情就是看看它是否是我们所需要的那个元素了。

function editCell(e) {
var target = getEventTarget(e);
if(target.tagName.toLowerCase() == 'td') {
// DO SOMETHING WITH THE CELL
}
}

在editCell函数中,我们通过检查目标元素标签名称的方法来确定它是否是一个表格的单元格。

事件冒泡及捕获

之前的介绍中已经说到了浏览器的事件冒泡机制。这里再详细介绍一下浏览器处理DOM事件的过程。对于事件的捕获和处理,不同的浏览器厂商有不同的处理机制,这里我们主要介绍W3C对DOM2.0定义的标准事件。

DOM2.0模型将事件处理流程分为三个阶段:一、事件捕获阶段,二、事件目标阶段,三、事件起泡阶段。如图:

事件捕获:当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的。

事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。

事件起泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。如果想阻止事件起泡,可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)来组织事件的冒泡传播。

举个栗子?

假设有一个 UL 的父节点,包含了很多个 Li 的子节点:

<ul id="parent-list">
<li id="post-1">Item 1</li>
<li id="post-2">Item 2</li>
<li id="post-3">Item 3</li>
<li id="post-4">Item 4</li>
<li id="post-5">Item 5</li>
<li id="post-6">Item 6</li>
</ul>

我们通常的做法是对每一个元素进行循环操作添加监听事件:

var oUl=document.getElementById("parent-list");
var aLi=oUl.getElementsByTagName('li'); for (var i = 0; i < aLi.length; i++) {
aLi[i].onclick=function () {
console.log(this.id);
}
}

如果这个UL中的Li子元素会频繁地添加或者删除,我们就需要在每次添加Li添加事件处理函数,这就增加了复杂度和出错的可能性。

更简单的方法是使用事件代理机制,当事件被抛到更上层的父节点的时候,我们通过检查事件的目标对象(target)来判断并获取事件源Li。下面的代码可以完成我们想要的效果:

var oUl=document.getElementById("parent-list");
var aLi=oUl.getElementsByTagName('li'); function target(e) {
var oEvent=e||event;
return oEvent.target||oEvent.srcElement;
} oUl.addEventListener('click',function (e) {
var oEvent=e||event;
var targets=target(oEvent);
if (targets.tagName.toLowerCase()=='li') {
console.log(targets.id);
}
})

为父节点添加一个click事件,当子节点被点击的时候,click事件会从子节点开始向上冒泡。父节点捕获到事件之后,通过判断e.target.nodeName来判断是否为我们需要处理的节点。并且通过e.target拿到了被点击的Li节点。从而可以获取到相应的信息,并作处理。

jQuery中delegate函数

下面看一下jQuery中提供的事件代理接口的使用方法。

$("#link-list").delegate("a", "click", function(){
// "$(this)" is the node that was clicked
console.log("you clicked a link!",$(this));
});

jQuery的delegate的方法需要三个参数,一个选择器,一个事件名称,和事件处理函数。

优点、缺点

通过上面的介绍,大家应该能够体会到使用事件委托对于web应用程序带来的几个优点:

1.管理的函数变少了。不需要为每个元素都添加监听函数。对于同一个父节点下面类似的子元素,可以通过委托给父元素的监听函数来处理事件。

2.可以方便地动态添加和修改元素,不需要因为元素的改动而修改事件绑定。

3.JavaScript和DOM节点之间的关联变少了,这样也就减少了因循环引用而带来的内存泄漏发生的概率。

潜在的问题也许并不那么明显,但是一旦你注意到这些问题,你就可以轻松地避免它们:

你的事件管理代码有成为性能瓶颈的风险,所以尽量使它能够短小精悍。

不是所有的事件都能冒泡的。blur、focus、load和unload不能像其它事件一样冒泡。事实上blur和focus可以用事件捕获而非事件冒泡的方法获得(在IE之外的其它浏览器中)。

在管理鼠标事件的时候有些需要注意的地方。如果你的代码处理mousemove事件的话你遇上性能瓶颈的风险可就大了,因为mousemove事件触发非常频繁。而mouseout则因为其怪异的表现而变得很难用事件代理来管理。

在JavaScript编程中使用代理

上面介绍的是对DOM事件处理时,利用浏览器冒泡机制为DOM元素添加事件代理。其实在纯JS编程中,我们也可以使用这样的编程模式,来创建代理对象来操作目标对象。这里引用司徒正美相关文章中的一个例子:

var delegate = function(client, clientMethod) {
return function() {
return clientMethod.apply(client, arguments);
}
}
var ClassA = function() {
var _color = "red";
return {
getColor: function() {
console.log("Color: " + _color);
},
setColor: function(color) {
_color = color;
}
};
}; var a = new ClassA();
a.getColor();
a.setColor("green");
a.getColor();
console.log("执行代理!");
var d = delegate(a, a.setColor);
d("blue");
console.log("执行完毕!");
a.getColor();

上面的例子中,通过调用delegate()函数创建的代理函数d来操作对a的修改。这种方式尽管是使用了apply(call也可以)来实现了调用对象的转移,但是从编程模式上实现了对某些对象的隐藏,可以保护这些对象不被随便访问和修改。

在很多框架中都引用了委托这个概念用来指定方法的运行作用域。比较典型的如dojo.hitch(scope,method)和ExtJS的createDelegate(obj,args)。有兴趣的同学可以看一下他们的源代码,主要也是js函数的apply方法来制定执行作用域。(其实我对于上面这个栗子,不是特别理解是如何运用的事件代理,能够深刻理解的童鞋请帮我留言讲解一下,谢谢)

总结:

已经有一些使用主流类库的事件代理示例出现了,比如说jQuery、Prototype以及Yahoo! UI。你也可以找到那些不用任何类库的例子,比如说Usable Type blog上的这一个。一旦需要的话,事件代理将是你工具箱里的一件得心应手的工具,而且它很容易实现。

本博客是转自两篇文章,博主结合自己的理解,整理出这篇博文,原文链接如下:

http://blog.csdn.net/weinideai/archive/2009/01/19/3835839.aspx

http://www.cnblogs.com/owenChen/archive/2013/02/18/2915521.html

JS 事件代理的更多相关文章

  1. js事件代理(委托)

    JavaScript事件代理(委托)一般用于以下情况: 1. 事件注册在祖先级元素上,代理其子级元素.可以减少事件注册数量,节约内存开销,提高性能. 2. 对js动态添加的子元素可自动绑定事件. 之前 ...

  2. js事件代理

    需要注意的blog:http://blog.csdn.net/majian_1987/article/details/8591385 一篇博客看懂  http://blog.csdn.net/maji ...

  3. 封装Js事件代理方法

    // 封装事件代理 function delegateEvent(element, tag, event, listener) { // 判断是否支持addEventlistener if(eleme ...

  4. 关于JS事件冒泡与JS事件代理(事件委托)

    连接:https://blog.csdn.net/supercoooooder/article/details/52190100 核心代码: <ul id="parentUl" ...

  5. js中的事件委托或是事件代理详解

    起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...

  6. JS中事件代理与委托

    在javasript中delegate这个词经常出现,看字面的意思,代理.委托.那么它究竟在什么样的情况下使用?它的原理又是什么?在各种框架中,也经常能看到delegate相关的接口.这些接口又有什么 ...

  7. JS中的事件委托(事件代理)

    一步一步来说说事件委托(或者有的资料叫事件代理) js中事件冒泡我们知道,子元素身上的事件会冒泡到父元素身上. 事件代理就是,本来加在子元素身上的事件,加在了其父级身上. 那就产生了问题:父级那么多子 ...

  8. [转] js中的事件委托或是事件代理详解

    起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...

  9. 【转】js中的事件委托或是事件代理详解

    起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...

随机推荐

  1. 功能齐全并且比较时髦的Jquery通用开源框架之【ejq.js】

    简介 ejq是一款非常小巧的JS工具库,未压缩才50K,在jquery的基础上对jquery缺失部分作了很好的弥补作用. 优点: 1.具有内置的模板解析引擎语法和angularjs相近减少学习成本 2 ...

  2. asp.net mvc5+Echarts3.0+AspNet.SignalR2.0 实时监控cpu占用率推送

    先看看效果: 感谢QQ群18668853的群主,无私的公开课,全是干货,当初学的时候,群主用的是jquery.flot做图表,我这里改成百度的Echarts3.0,这算是自己的修改吧,喜欢的可以去下载 ...

  3. 【原创】kafka producer源代码分析

        Kafka 0.8.2引入了一个用Java写的producer.下一个版本还会引入一个对等的Java版本的consumer.新的API旨在取代老的使用Scala编写的客户端API,但为了兼容性 ...

  4. HTML5使用Div标签来实现表格

    当支持HTML5时,我们可以使用样式把DIV实现一个表格table. 关键样式语法:display: table,display: table-row,display: table-cell 下面例子 ...

  5. Web API应用架构在Winform混合框架中的应用(2)--自定义异常结果的处理

    在上篇随笔<Web API应用架构在Winform混合框架中的应用(1)>中我介绍了关于如何在Winfrom里面整合WebAPI,作为一个新型数据源的接入方式,从而形成了三种不同的数据提供 ...

  6. 年薪10w和年薪100w的人,差在哪里?

    职场10年,为什么有人已经当上了董事总经理,而有的人还是资深销售经理? 出道10年,为什么有人已经当上了主编.出版人,而有的人还是资深编辑? 打拼10年,为什么有人已经身价数十亿美金,而有的人还在为竞 ...

  7. HoverTree项目添加了查看留言列表功能

    HoverTree项目添加了查看留言列表功能 页面:HoverTreeWeb项目下hvtpanel/usermessage/messagelist.aspx 添加留言页面:addmessage.asx ...

  8. 【工具】清理Windows Installer冗余文件(支持64位NT6.x系统)

    样子: 支持系统: Windows NT 5.x/6.x 32及64位所有系统.需.net framework 2.0运行环境 功能: 清理上述系统中冗余的Windows Installer补丁文件. ...

  9. iOS 阶段学习第23天笔记(XML数据格式介绍)

    iOS学习(OC语言)知识点整理 一.XML数据格式介绍 1)概念:xml是extensible markup language扩展的标记语言,一般用来表示.传输和存储数据 2)xml与json目前使 ...

  10. script加载文件