JavaScript事件代理入门
事件代理(Event Delegation),又称之为事件委托。是 JavaScript 中常用绑定事件的常用技巧。
顾名思义,“事件代理”即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。
为什么要这样做呢?
众所周知,DOM操作是十分消耗性能的。所以重复的事件绑定简直是性能杀手。而事件代理的核心思想,就是通过尽量少的绑定,去监听尽量多的事件。
下面将会用 Zepto 为大家演示怎么实现事件代理。
啊?Zepto是什么?
Zepto is a minimalist JavaScript library for modern browsers with a largely jQuery-compatible API. If you use jQuery, you already know how to use Zepto.
由于API是兼容 jQuery 的,熟悉jQuery的童鞋使用 Zepto 几乎无需学习成本。演示代码实际上也能在 jQuery 上正常运行。而目前 Zepto 的适用场景更多是在移动端,一个远离旧版IE的世界。
那为什么不直接用jQuery?
因为下文会简要分析源码,但jQuery里面为了兼容旧版IE做了很多妥协,源码十分不直观。而 Zepto 是面向现代浏览器设计的,所用到的API绝大多数都符合W3C标准,分析起来更加直观。
以这个HTML结构为例:
<ul class="list">
<li class="list_items">000</li>
<li class="list_items">111 <a href="javascript:void(0);">link</a></li>
<li class="list_items">222 <i>italic</i></li>
<li>333</li>
</ul>
一般情况下,会这样绑定事件:
$('.list_items').on('click', function (e) {
console.log(e.target.tagName);
console.log(this.tagName);
});
打开浏览器的 Console 看一下,会发现每一个 .list_items 元素都被绑定了click事件,并且绑定的对象是 li.list_items 。如下图:

这意味着,Zepto的实际上是遍历了所有 .list_items 元素,并逐个绑定 click 事件。
实现思路和下面这段原生 JavaScript 代码相同:
[].forEach.call(document.querySelectorAll('.list_items'), function (elem) {
elem.addEventListener('click', function (e) {
console.log(e.target.tagName);
console.log(this.tagName);
}, false);
});
这样的做法,当遇到数量超长的列表(ul)和表格(table)时性能开销非常大。
例如:ul 中有1000个 li 时,就需要进行1000次的事件绑定。
而事件代理,就是应用于这种场景的。
我们先看一下Zepto官方的API文档:

on 方法还支持在 回调函数 前传入一个 [selector] 的值,而这个 [selector] 就是实际需要监听事件的的元素。
看看实际例子:
$('.list').on('click', '.list_items', function (e) {
console.log(e.target.tagName);
console.log(this.tagName);
});
通过 on 方法把 click 事件代理在 ul.list 元素上,监听其所有 .list_items 的子元素的点击操作。
打开浏览器的 Console 看看:

虽然提示每个 .list_items 都有事件监听,但它们的绑定对象都是指向 ul.list 。
事件代理是通过什么机制实现的?
这其中的核心思想是 事件冒泡(Event Bubble) 。但在这里我不打算细说事件冒泡,因为这会是一个很大的话题。Google一下,就能找到有很多相关的介绍了。
下面我们看一下 Zepto 的源码,它是怎么去处理这个事件代理的。
方法 on 的实现:
$.fn.on = function(event, selector, data, callback, one){
var autoRemove, delegator, $this = this
if (event && !isString(event)) {
$.each(event, function(type, fn){
$this.on(type, selector, data, fn, one)
})
return $this
}
if (!isString(selector) && !isFunction(callback) && callback !== false)
callback = data, data = selector, selector = undefined
if (isFunction(data) || data === false)
callback = data, data = undefined
if (callback === false) callback = returnFalse
return $this.each(function(_, element){
if (one) autoRemove = function(e){
remove(element, e.type, callback)
return callback.apply(this, arguments)
}
if (selector) delegator = function(e){
var evt, match = $(e.target).closest(selector, element).get(0)
if (match && match !== element) {
evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))
}
}
add(element, event, callback, data, selector, delegator || autoRemove)
})
}
代码的大致执行流程如下:

由上面的活动图可以看出,大部分逻辑都是用来处理 on 方法传入参数,delegator 函数是事件代理的关键。
至于 add 方法,主要是对某些特殊事件进行处理,例如:ready、hover等。当成黑盒去看待,它就等同于原生的 addEventListener 方法。
下面再细分一下 delegator 函数的实现逻辑:

到这里,大家应该都清楚了。
调用 on 方法进行事件绑定时,只有传入 [selector] 参数才会实现事件代理。
实际应用中,如何用 JavaScript 原生代码写出事件绑定呢?如下:
var listElem = document.querySelector('.list');
listElem.addEventListener('click', function (e) {
var delegateTarget = this,
fireTarget = e.target,
eventTarget;
if (fireTarget.className !== 'list_items') {
function findParent(elem) {
if (elem === delegateTarget) {
return null;
}
var parent = elem.parentNode;
if (parent.className === 'list') {
return null;
}
if (parent.className === 'list_items') {
return parent;
}
findParent(parent);
}
eventTarget = findParent(fireTarget);
} else {
eventTarget = fireTarget;
}
if (!eventTarget) return false;
console.log('fireTarget: ' + fireTarget.tagName);
console.log('eventTarget: ' + eventTarget.tagName);
}, false);
以上。
参考资料:
本文作者:Maple Jan
本文链接:http://www.cnblogs.com/maplejan/p/3761519.html
JavaScript事件代理入门的更多相关文章
- JavaScript事件代理和委托(Delegation)
JavaScript事件代理 首先介绍一下JavaScript的事件代理.事件代理在JS世界中一个非常有用也很有趣的功能.当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委 ...
- JavaScript事件代理和委托
在javasript中,代理.委托经常出现. 那么它究竟在什么样的情况下使用?它的原理又是什么? 这里介绍一下javascript delegate的用法和原理,以及Dojo,jQuery等框架中de ...
- JavaScript 事件代理
转自:http://www.cnblogs.com/silence516/archive/2009/09/03/delegateEvent.html 如果你想给网页添加点JavaScript的交互性, ...
- 【转载】浅谈事件冒泡与事件捕获 - javascript 事件代理
原文:https://segmentfault.com/a/1190000000749838 事件冒泡与事件捕获 事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发 ...
- JavaScript事件代理和事件委托
一.概述: 那什么叫事件委托呢?它还有一个名字叫事件代理,JavaScript高级程序设计上讲:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件.那这是什么意思呢?网上的 ...
- javascript事件代理(委托)
之前有接触过事件代理,但是印象并不深刻.这次记下来加强印象. 用个大家比较常见的代码举例子: html dom结构: <ul id="ul1"> <li>0 ...
- javascript 事件代理及应用
事件代理又叫事件委托在前端发开中实际是非常有用的,说事件代理之前我们先说说事件绑定 <p onclick="test()" ></p> function t ...
- javascript事件代理(Event Delegation)
看了几篇文章,放上来供参考 司徒正美的文章,Event Delegation Made Easy --------------------------------------------------- ...
- JavaScript 事件代理绑定
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
随机推荐
- Akka系列---什么是Actor
本文已.Net语法为主,同时写有Scala及Java实现代码 严肃的说,演员是一个广泛的概念,作为外行人我对Actor 模型的定义: Actor是一个系统中参与者的虚拟人物,Actor与Actor之间 ...
- Android开发中常见的内存泄露案例以及解决方法总结
1.单例模式引起的内存泄露 由于单例模式的静态特性,使得它的生命周期和我们的应用一样长,如果让单例无限制的持有Activity的强引用就会导致内存泄漏如错误代码示例: public class Use ...
- [2017BUAA软工]第3次个人作业
软工第3次个人作业--案例分析 一. 调研,评测 1.软件的bug(至少两个,不少于40字) 测试软件: 必应词典移动端 测试平台:iPhone 6 bug1 对于翻译功能中的图片翻译功能,必应词典是 ...
- [转发]VMware厚置备延迟置零 、 厚置备置零、精简置备 区别
1.厚置备延迟置零(zeroed thick) 以默认的厚格式创建虚拟磁盘.创建过程中为虚拟磁盘分配所需空间.创建时不会擦除物理设备上保留的任何数据,但是以后从虚拟机首次执行写操作时会按需要将其置零. ...
- 绿色计算大赛决赛 第二阶段 消息传递(斯坦纳树 状压dp+spfa)
传送门 Description 作为公司老板的你手下有N个员工,其中有M个特殊员工.现在,你有一个消息需要传递给你的特殊员工.因为你的公司业务非常紧张,所以你和员工之间以及员工之间传递消息会造成损失. ...
- 静态属性加载到jvm时候就存放在数据共享区,而不是等new后出现
静态属性加载到jvm时候就存放在数据共享区,而不是等new后出现.他的生命周期是 jvm结束 才会消失,一般的方法属性是对象结束后 就会消失.
- Redis 基础:Redis 配置
Redis 配置 Redis的配置文件位于Redis安装目录下,文件名为redis.conf.可以通过CONFIG命令查看或设置配置项.其语法为: # Redis CONFIG命令格式如下: > ...
- Ubuntu 12.04下LAMP环境搭建实录
方法一: 用tasksel可以方便安装dns server, lamp, kubuntu desktop, ubuntu desktop, xubuntu之类的软件包.这个软件在ubuntu serv ...
- 题解 P4379 【[USACO18OPEN]Lemonade Line】
不敢快速排序又想要快排的速度,还不用STL的小伙伴们看这里! 小金羊终于学会了堆排以外的另外的一种排序 (打个题解巩固一下) 归并排序(mergesort): 时间复杂度和快排一样的优秀. 先说归并排 ...
- Jmeter—添加断言 判断接口响应数据是否符合预期
发出请求之后,通过添加断言可以判断响应数据是否是我们的预期结果. 1 在Jmeter中发送一个状态返回200的http请求(参数故意输入错误).结果肯定是不是返回200啦. 但结果树中http请求的图 ...