详解js的事件代理(委托)


Posted in Javascript onDecember 22, 2016

JavaScript事件代理(委托)一般用于以下情况:

  1. 事件注册在祖先级元素上,代理其子级元素。可以减少事件注册数量,节约内存开销,提高性能。

  2. 对js动态添加的子元素可自动绑定事件。

之前一直用各种js库的事件代理,如 jQuery,非常方便实用。今天尝试用原生 js 实现该功能。

var addEvent = (function () {
 if (document.addEventListener) {
 return function (element, type, handler) {
 element.addEventListener(type, handler, false);
 };
 } else if (document.attachEvent) {
 return function (element, type, handler) {
 element.attachEvent('on' + type, function () {
 handler.apply(element, arguments);
 });
 };
 } else {
 return function (element, type, handler) {
 element['on' + type] = function () {
 return handler.apply(element, arguments);
 };
 };
 }
})(),
getClassElements = function (parentElement, classname) {
 var all, element, classArr = [], classElements = [];
 if (parentElement.getElementsByClassName) {
 return parentElement.getElementsByClassName(classname);
 } else {
 all = parentElement.getElementsByTagName('*');
 for (var i = 0, len = all.length; i < len; i++) {
 element = all[i];
 classArr = element && element.className && element.className.split(' ');
 if (classArr) {
 for (var j = 0; j < classArr.length; j++) {
  if (classArr[j] === classname) {
  classElements.push(element);
  }
 }
 }
 }
 return classElements;
 }
},
delegate = function () { // 参数:element, type, [selector,] handler
 var args = arguments,
 element = args[0],
 type = args[1],
 handler;
 if (args.length === 3) {
 handler = args[2];
 return addEvent(element, type, handler);
 }
 if (args.length === 4) {
 selector = args[2];
 handler = args[3];
 return addEvent(element, type, function (event) {
 var event = event || window.event,
 target = event.target || event.srcElement,
 quickExpr = /^(?:[a-zA-Z]*#([\w-]+)|(\w+)|[a-zA-Z]*\.([\w-]+))$/,
 match,
 idElement,
 elements,
 tagName,
 count = 0,
 len;
 if (typeof selector === 'string') {
 match = quickExpr.exec(selector);
 if (match) {
  // #ID selector
  if (match[1]) {
  idElement = document.getElementById(match[1]);
  tagName = match[0].slice(0, match[0].indexOf('#'));
  // tag selector
  } else if (match[2]) {
  elements = element.getElementsByTagName(selector);
  // .class selector
  } else if (match[3]) {
  elements = getClassElements(element, match[3]);
  tagName = match[0].slice(0, match[0].indexOf('.'));
  }
 }
 if (idElement) {
  if ( tagName ? tagName === idElement.nodeName.toLowerCase() && target === idElement : target === idElement ) {
  return handler.apply(idElement, arguments);
  }
 } else if (elements) {
  for (len = elements.length; count < len; count++) {
  if ( tagName ? tagName === elements[count].nodeName.toLowerCase() && target === elements[count] : target === elements[count] ) {
  return handler.apply(elements[count], arguments);
  }
  }
 }
 }
 });
 }
};

主要是用 apply 改变 this 的指向

handler.apply(idElement, arguments);
handler.apply(elements[count], arguments);

测试一下:

<style>
#outer {padding: 50px; background-color: lightpink;}
#inner {padding: 30px; background-color: aliceblue;}
#paragraph1, #paragraph3 {background-color: cadetblue}
</style>
<div id="outer">outer
 <div id="inner">inner
 <p id="paragraph1" class="parag1">paragraph1</p>
 <p id="paragraph2" class="parag">paragraph2</p>
 <span>span</span>
 <p id="paragraph3" class="parag">paragraph3</p>
 </div>
</div>
var outer = document.getElementById('outer');
delegate(outer, 'click', function () {
 console.log(this.id); // outer
});
delegate(outer, 'click', 'p', function () {
 console.log(this.id); //点击 paragraph1 元素,输出其id为 "paragraph1"
});

模仿 jQuery 的风格,优化代码:

(function () {
 var $ = function (element) {
 return new _$(element);
 };
 var _$ = function (element) {
 this.element = element && element.nodeType === 1 ? element : document;
 };
 _$.prototype = {
 constructor: _$,
 addEvent: function (type, handler, useCapture) {
 var element = this.element;
 if (document.addEventListener) {
 element.addEventListener(type, handler, (useCapture ? useCapture : false));
 } else if (document.attachEvent) {
 element.attachEvent('on' + type, function () {
  handler.apply(element, arguments);
 });
 } else {
 element['on' + type] = function () {
  return handler.apply(element, arguments);
 };
 }
 return this;
 },
 getClassElements: function (classname) {
 var element = this.element, all, ele, classArr = [], classElements = [];
 if (element.getElementsByClassName) {
 return element.getElementsByClassName(classname);
 } else {
 all = element.getElementsByTagName('*');
 for (var i = 0, len = all.length; i < len; i++) {
  ele = all[i];
  classArr = ele && ele.className && ele.className.split(' ');
  if (classArr) {
  for (var j = 0; j < classArr.length; j++) {
  if (classArr[j] === classname) {
  classElements.push(ele);
  }
  }
  }
 }
 return classElements;
 }
 },
 delegate: function () { //参数:type, [selector,] handler
 var self = this,
 element = this.element,
 type = arguments[0],
 handler;
 if (arguments.length === 2) {
 handler = arguments[1];
 return self.addEvent(type, handler);
 } else if (arguments.length === 3) {
 selector = arguments[1];
 handler = arguments[2];
 return self.addEvent(type, function (event) {
  var event = event || window.event,
  target = event.target || event.srcElement,
  quickExpr = /^(?:[a-zA-Z]*#([\w-]+)|(\w+)|[a-zA-Z]*\.([\w-]+))$/,
  match,
  idElement,
  elements,
  tagName,
  count = 0,
  len;
  if (typeof selector === 'string') {
  match = quickExpr.exec(selector);
  if (match) {
  // #ID selector
  if (match[1]) {
  idElement = document.getElementById(match[1]);
  tagName = match[0].slice(0, match[0].indexOf('#'));
  // tag selector
  } else if (match[2]) {
  elements = element.getElementsByTagName(selector);
  // .class selector
  } else if (match[3]) {
  elements = self.getClassElements(match[3]);
  tagName = match[0].slice(0, match[0].indexOf('.'));
  }
  }
  if (idElement) {
  if ( tagName ? tagName === idElement.nodeName.toLowerCase() && target === idElement ? target === idElement ) {
  return handler.apply(idElement, arguments);
  }
  } else if (elements) {
  for (len = elements.length; count < len; count++) {
  if ( tagName ? tagName === elements[count].nodeName.toLowerCase() && target === elements[count] : target === elements[count] ) {
   return handler.apply(elements[count], arguments);
  }
  }
  }
  }
 });
 }
 }
 };
 window.$ = $;
 return $;
}());

使用如下:

var outer = document.getElementById('outer');
$(outer).delegate('click', '.parag', function (event) {
 console.log(this.id);
});

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
javascript 限制输入脚本大全
Nov 03 Javascript
Javascript中的isNaN函数使用说明
Nov 10 Javascript
关于jquery css的使用介绍
Apr 18 Javascript
js中window.open打开一个新的页面
Aug 10 Javascript
javaScript中with函数用法实例分析
Jun 08 Javascript
jQuery 生成svg矢量二维码
Aug 09 Javascript
Vue.js学习笔记之 helloworld
Aug 14 Javascript
jQuery实现圣诞节礼物动画案例解析
Dec 25 Javascript
对vue.js中this.$emit的深入理解
Feb 23 Javascript
Vue使用json-server进行后端数据模拟功能
Apr 17 Javascript
Node.js实现用户评论社区功能(体验前后端开发的乐趣)
May 09 Javascript
Element-ui upload上传文件限制的解决方法
Jan 22 Javascript
HTML页面定时跳转方法解析(2种任选)
Dec 22 #Javascript
vue双向绑定的简单实现
Dec 22 #Javascript
jQuery使用Layer弹出层插件闪退问题
Dec 22 #Javascript
深入理解jquery中extend的实现
Dec 22 #Javascript
jQuery插件DataTable使用方法详解(.Net平台)
Dec 22 #Javascript
JS实现间歇滚动的运动效果实例
Dec 22 #Javascript
javascript-解决mongoose数据查询的异步操作
Dec 22 #Javascript
You might like
定义php常量的详解
2013/06/09 PHP
PHP使用feof()函数读文件的方法
2014/11/07 PHP
php通过ksort()函数给关联数组按照键排序的方法
2015/03/18 PHP
ThinkPHP框架分布式数据库连接方法详解
2017/03/14 PHP
使用jQuery.Validate进行客户端验证(初级篇) 不使用微软验证控件的理由
2010/06/28 Javascript
jquery.boxy弹出框(后隔N秒后自动隐藏/自动跳转)
2013/01/15 Javascript
js、css、img等浏览器缓存问题的2种解决方案
2013/10/23 Javascript
删除节点的jquery代码
2014/01/13 Javascript
关于onchange事件在IE和FF下的表现及解决方法
2014/03/08 Javascript
js实现鼠标滚轮控制图片缩放效果的方法
2015/02/20 Javascript
基于jquery实现轮播特效
2016/04/22 Javascript
使用vue.js开发时一些注意事项
2016/04/27 Javascript
javascript简单实现等比例缩小图片的方法
2016/07/27 Javascript
如何实现星星评价(jquery.raty.js插件)
2016/12/21 Javascript
jQuery实现为动态添加的元素绑定事件实例分析
2018/09/07 jQuery
JavaScript 对引擎、运行时、调用堆栈的概述理解
2018/10/22 Javascript
Vue将页面导出为图片或者PDF
2020/08/17 Javascript
详解BootStrap表单验证中重置BootStrap-select验证提示不清除的坑
2019/09/17 Javascript
微信小程序动态评分展示/五角星展示/半颗星展示/自定义长度展示功能的实现
2020/07/22 Javascript
Python 实现「食行生鲜」签到领积分功能
2018/09/26 Python
python列表list保留顺序去重的实例
2018/12/14 Python
opencv python统计及绘制直方图的方法
2019/01/21 Python
python实现两个dict合并与计算操作示例
2019/07/01 Python
使用pandas读取文件的实现
2019/07/31 Python
Python 矩阵转置的几种方法小结
2019/12/02 Python
numpy库ndarray多维数组的维度变换方法(reshape、resize、swapaxes、flatten)
2020/04/28 Python
django正续或者倒序查库实例
2020/05/19 Python
Django中的模型类设计及展示示例详解
2020/05/29 Python
StubHub美国:购买或出售您的门票
2019/07/09 全球购物
SIMON MILLER官网:洛杉矶的生活方式品牌
2020/10/19 全球购物
小学生自我评价范例
2013/09/24 职场文书
毕业生文员求职信
2013/11/03 职场文书
机械设备与数控技术专业求职信
2014/08/10 职场文书
2015年监理工作总结范文
2015/04/07 职场文书
2015年煤矿工作总结
2015/04/28 职场文书
Java用自带的Image IO给图片添加水印
2021/06/15 Java/Android