JavaScript事件代理和委托详解


Posted in Javascript onApril 08, 2016

在javasript中,代理、委托经常出现。

那么它究竟在什么样的情况下使用?它的原理又是什么?

这里介绍一下javascript delegate的用法和原理,以及Dojo,jQuery等框架中delegate的接口。

JavaScript事件代理
事件代理在JS世界中一个非常有用也很有趣的功能。当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委托给父节点来触发处理函数。

这主要得益于浏览器的事件冒泡机制,下面我们具体举个例子来解释如何使用这个特性。

这个例子主要取自David Walsh的相关文章(How JavaScript Event Delegation Works)。

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

<ul id="list">
 <li id="li-1">Li 1</li>
 <li id="li-2">Li 2</li>
 <li id="li-3">Li 3</li>
 <li id="li-4">Li 4</li>
 <li id="li-5">Li 5</li> 
</ul>

当我们的鼠标移到Li上的时候,需要获取此Li的相关信息并飘出悬浮窗以显示详细信息,或者当某个Li被点击的时候需要触发相应的处理事件。

我们通常的写法,是为每个Li都添加一些类似onMouseOver或者onClick之类的事件监听。

function addListenersLi(liElement) {
  liElement.onclick = function clickHandler() {
   //TODO
  };
  liElement.onmouseover = function mouseOverHandler() {
   //TODO
  }
 }

 window.onload = function() {
  var ulElement = document.getElementById("list");
  var liElements = ulElement.getElementByTagName("Li");
   for (var i = liElements.length - 1; i >= 0; i--) {
    addListenersLi(liElements[i]);
   } 
 }

如果这个UL中的Li子元素会频繁地添加或者删除,我们就需要在每次添加Li的时候都调用这个addListenersLi方法来为每个Li节点添加事件处理函数。

这会造成添加或者删除过程的复杂度和出错的可能性。

解决问题方法是使用事件代理机制,当事件被抛到更上层的父节点的时候,我们通过检查事件的目标对象(target)来判断并获取事件源Li。

下面的代码可以完成想要的效果: 

/ 获取父节点,并为它添加一个click事件
document.getElementById("list").addEventListener("click",function(e) {
 // 检查事件源e.targe是否为Li
 if(e.target && e.target.nodeName.toUpperCase == "LI") {
 // 
 //TODO
 console.log("List item ",e.target.id," was clicked!");
 }
});

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

事件冒泡及捕获
浏览器的事件冒泡机制,对于事件的捕获和处理,不同的浏览器厂商有不同的处理机制,这里介绍W3C对DOM2.0定义的标准事件。

DOM2.0模型将事件处理流程分为三个阶段:

一、事件捕获阶段,

二、事件目标阶段,

三、事件起泡阶段。

如下图:

JavaScript事件代理和委托详解

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

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

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

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

jQuery:

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

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

而Dojo的与jQuery相似,仅是两者的编程风格上的差别:

require(["dojo/query","dojox/NodeList/delegate"], function(query,delegate){

 query("#list").delegate("li","onclick",function(event) {
 // "this.node" is the node that was clicked
 console.log("you clicked a link!",this);
 });
})

Dojo的delegate模块在dojox.NodeList中,提供的接口与jQuery一样,参数也相同。

通过委托, 能够体会到使用事件委托对于开发带来的几个好处:

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

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

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

在JavaScript编程中使用代理
上面介绍的是对DOM事件处理时,利用浏览器冒泡机制为DOM元素添加事件代理。其实在纯JS编程中,我们也可以使用这样的编程模式,来创建代理对象来操作目标对象.

var delegate = function(client, clientMethod) {
  return function() {
   return clientMethod.apply(client, arguments);
  }
 }
 var Apple= function() {
  var _color = "red";
  return {
   getColor: function() {
    console.log("Color: " + _color);
   },
   setColor: function(color) {
    _color = color;
   }
  };
 };

 var a = new Apple();
 var b = new Apple();
 a.getColor();
 a.setColor("green");
 a.getColor();
 //调用代理
 var d = delegate(a, a.setColor);
 d("blue");
 //执行代理
 a.getColor();
 //b.getColor();

上面的例子中,通过调用delegate()函数创建的代理函数d来操作对a的修改。

这种方式尽管是使用了apply(call也可以)来实现了调用对象的转移,但是从编程模式上实现了对某些对象的隐藏,可以保护这些对象不被随便访问和修改。

在很多框架中都引用了委托这个概念用来指定方法的运行作用域。

比较典型的如dojo.hitch(scope,method)和ExtJS的createDelegate(obj,args)。

以上就是本文的全部内容,希望对大家学习javascript程序设计有所帮助。

Javascript 相关文章推荐
用jquery实现的模拟QQ邮箱里的收件人选取及其他效果(一)
Jan 06 Javascript
jquery多行滚动/向左或向上滚动/响应鼠标实现思路及代码
Jan 23 Javascript
兼容IE和Firefox火狐的上下、左右循环无间断滚动JS代码
Apr 19 Javascript
JS控制图片等比例缩放的示例代码
Dec 24 Javascript
node.js中的events.emitter.removeAllListeners方法使用说明
Dec 10 Javascript
jquery UI Datepicker时间控件的使用方法(基础版)
Nov 07 Javascript
JavaScript浏览器对象模型BOM(BrowserObjectModel)实例详解
Nov 29 Javascript
最常用的jQuery表单验证(简单)
May 23 jQuery
Vue.js实现一个todo-list的上移下移删除功能
Jun 26 Javascript
jQuery zTree搜索-关键字查询 递归无限层功能实现代码
Jan 25 jQuery
vue 使用axios 数据请求第三方插件的使用教程详解
Jul 05 Javascript
原生JS实现萤火虫效果
Mar 07 Javascript
javascript高级选择器querySelector和querySelectorAll全面解析
Apr 07 #Javascript
关于cookie的初识和运用(js和jq)
Apr 07 #Javascript
纯js实现瀑布流布局及ajax动态新增数据
Apr 07 #Javascript
原生JavaScript实现Ajax的方法
Apr 07 #Javascript
JavaScript数据推送Comet技术详解
Apr 07 #Javascript
js实现商品抛物线加入购物车特效
Nov 18 #Javascript
js类式继承与原型式继承详解
Apr 07 #Javascript
You might like
php中设置多级目录session的问题
2011/08/08 PHP
PHP文字转图片功能原理与实现方法分析
2017/08/31 PHP
laravel框架创建授权策略实例分析
2019/11/22 PHP
PHP设计模式之装饰器(装饰者)模式(Decorator)入门与应用详解
2019/12/13 PHP
jQuery新闻滚动插件 jquery.roller.js
2011/06/27 Javascript
fancybox modal的完美解决(右上的X)
2012/10/30 Javascript
Extjs Label的 fieldLabel和html属性值对齐的方法
2014/06/15 Javascript
javascript比较两个日期相差天数的方法
2015/07/24 Javascript
JavaScript判断对象是否为数组
2015/12/22 Javascript
第九章之路径分页标签与徽章组件
2016/04/25 Javascript
一个用jquery写的判断div滚动条到底部的方法【推荐】
2016/04/29 Javascript
JavaScript 函数的执行过程
2016/05/09 Javascript
关于javascript的一些知识以及循环详解
2016/09/12 Javascript
网站发布后Bootstrap框架引用woff字体无法正常显示的解决方法
2016/11/24 Javascript
js自制图片放大镜功能
2017/01/24 Javascript
使用get方式提交表单在地址栏里面不显示提交信息
2017/02/21 Javascript
使用BootStrap实现标签切换原理解析
2017/03/14 Javascript
详谈表单重复提交的三种情况及解决方法
2017/08/16 Javascript
基于JS实现html中placeholder属性提示文字效果示例
2018/04/19 Javascript
Layui数据表格之获取表格中所有的数据方法
2018/08/20 Javascript
Vue循环组件加validate多表单验证的实例
2018/09/18 Javascript
vue项目前端错误收集之sentry教程详解
2019/05/27 Javascript
JavaScript展开操作符(Spread operator)详解
2019/07/20 Javascript
PyQt5 对图片进行缩放的实例
2019/06/18 Python
jenkins+python自动化测试持续集成教程
2020/05/12 Python
Python3 Tensorlfow:增加或者减小矩阵维度的实现
2020/05/22 Python
Python configparser模块封装及构造配置文件
2020/08/07 Python
Python私有属性私有方法应用实例解析
2020/09/15 Python
Python爬虫之Selenium鼠标事件的实现
2020/12/04 Python
css 如何让背景图片拉伸填充避免重复显示
2013/07/11 HTML / CSS
加拿大在线隐形眼镜专家:PerfectLens.ca
2016/11/19 全球购物
测绘工程个人的自我评价
2013/11/23 职场文书
应届行政管理专业个人自我评价
2013/12/28 职场文书
HR求职自荐信范文
2014/06/21 职场文书
2015年公司工作总结
2015/04/25 职场文书
Win11如何设置右键单击显示所有选项?Win11右键单击显示所有选项设置教程
2022/04/08 数码科技