JS 事件代理
事件处理器: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 事件代理的更多相关文章
- js事件代理(委托)
JavaScript事件代理(委托)一般用于以下情况: 1. 事件注册在祖先级元素上,代理其子级元素.可以减少事件注册数量,节约内存开销,提高性能. 2. 对js动态添加的子元素可自动绑定事件. 之前 ...
- js事件代理
需要注意的blog:http://blog.csdn.net/majian_1987/article/details/8591385 一篇博客看懂 http://blog.csdn.net/maji ...
- 封装Js事件代理方法
// 封装事件代理 function delegateEvent(element, tag, event, listener) { // 判断是否支持addEventlistener if(eleme ...
- 关于JS事件冒泡与JS事件代理(事件委托)
连接:https://blog.csdn.net/supercoooooder/article/details/52190100 核心代码: <ul id="parentUl" ...
- js中的事件委托或是事件代理详解
起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...
- JS中事件代理与委托
在javasript中delegate这个词经常出现,看字面的意思,代理.委托.那么它究竟在什么样的情况下使用?它的原理又是什么?在各种框架中,也经常能看到delegate相关的接口.这些接口又有什么 ...
- JS中的事件委托(事件代理)
一步一步来说说事件委托(或者有的资料叫事件代理) js中事件冒泡我们知道,子元素身上的事件会冒泡到父元素身上. 事件代理就是,本来加在子元素身上的事件,加在了其父级身上. 那就产生了问题:父级那么多子 ...
- [转] js中的事件委托或是事件代理详解
起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...
- 【转】js中的事件委托或是事件代理详解
起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...
随机推荐
- 05-Vue入门系列之Vue实例详解与生命周期
Vue的实例是Vue框架的入口,其实也就是前端的ViewModel,它包含了页面中的业务逻辑处理.数据模型等,当然它也有自己的一系列的生命周期的事件钩子,辅助我们进行对整个Vue实例生成.编译.挂着. ...
- 【Android】学习记录<1> -- 初识ffmpeg
工作需要用到ffmpeg来进行Android的软编码,对这玩意儿一点都不了解,做个学习记录先. FFmpeg:http://www.ffmpeg.org Fmpeg is the leading mu ...
- 200、301、302、304、404等HTTP状态码
在网站建设的实际应用中,容易出现很多小小的失误,就像mysql当初优化不到位,影响整体网站的浏览效果一样,其实,网站的常规http状态码的表现也是一样,Google无法验证网站几种解决办法,提及到由于 ...
- C#--访问修饰符
- 30天C#基础巩固----查找XML文件元素
一:XML文档 了解xml文档. 利用代码来创建XML文档. //引用命名空间+using System.Xml; XmlDocument xdoc=new XmlDocument(); XmlDec ...
- C语言学习014:结构化数据类型
struct的基本使用 #include <stdio.h> //定义数据结构 struct fish{ const char *name; const char *species; in ...
- java开源时间/日期库Joda-Time
任何企业应用程序都需要处理时间问题.应用程序需要知道当前的时间点和下一个时间点,有时它们还必须计算这两个时间点之间的路径.使用 JDK 完成这项任务将非常痛苦和繁琐.现在来看看 Joda Time,一 ...
- SQL常用语句(2)
//计算表tb_Blog的字段个数 select count(*) from syscolumns where id=object_id('tb_Blog') 获取指定表的所有字段和字段类型 SELE ...
- Ado.net[增删改查,GET传值]
1. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Home.aspx.c ...
- What Can Java Technology Do?
What Can Java Technology Do? The general-purpose(多用途的), high-level Java programming language is a po ...