详解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 相关文章推荐
jQuery Flash/MP3/Video多媒体插件
Jan 18 Javascript
Javascript模块化编程(一)AMD规范(规范使用模块)
Jan 17 Javascript
JS中for循序中延迟加载动态效果的具体实现
Aug 18 Javascript
jquery如何实现锚点链接之间的平滑滚动
Dec 02 Javascript
javascript中interval与setTimeOut的区别示例介绍
Mar 14 Javascript
JS实现跟随鼠标的链接文字提示框效果
Aug 06 Javascript
BootStrap.css 在手机端滑动时右侧出现空白的原因及解决办法
Jun 07 Javascript
JS模拟的Map类实现方法
Jun 17 Javascript
jQuery自制提示框tooltip改进版
Aug 01 Javascript
微信小程序数字滚动插件使用详解
Feb 02 Javascript
JavaScript学习笔记之基于定时器实现图片无缝滚动功能详解
Jan 09 Javascript
JS实现倒序输出的几种常用方法示例
Apr 13 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定时自动生成静态HTML的实现代码
2010/06/20 PHP
php 搜索框提示(自动完成)实例代码
2012/02/05 PHP
PHP之APC缓存详细介绍 apc模块安装
2014/01/13 PHP
thinkphp实现上一篇与下一篇的方法
2014/12/08 PHP
在Laravel框架里实现发送邮件实例(邮箱验证)
2016/05/20 PHP
完美解决在ThinkPHP控制器中命名空间的问题
2017/05/05 PHP
php通过pecl方式安装扩展的实例讲解
2018/02/02 PHP
网页前台通过js非法字符过滤代码(骂人的话等等)
2010/05/26 Javascript
javascrip客户端验证文件大小及文件类型并重置上传
2011/01/12 Javascript
js定义对象或数组直接量时各浏览器对多余逗号的处理(json)
2011/03/05 Javascript
轻松创建nodejs服务器(10):处理POST请求
2014/12/18 NodeJs
如何使用jQuery技术开发ios风格的页面导航菜单
2015/07/29 Javascript
如何屏蔽防止别的网站嵌入框架代码
2015/08/24 Javascript
又一款js时钟!transform实现时钟效果
2016/08/15 Javascript
JavaScript实现水平进度条拖拽效果
2017/01/18 Javascript
ztree实现权限横向显示功能
2017/05/20 Javascript
Node中使用ES6语法的基础教程
2018/01/05 Javascript
在Vue组件中使用 TypeScript的方法
2018/02/28 Javascript
Vue+Express实现登录注销功能的实例代码
2019/05/05 Javascript
vue中typescript装饰器的使用方法超实用教程
2019/06/17 Javascript
Javascript实现鼠标点击冒泡特效
2019/12/24 Javascript
使用 Github Actions 自动部署 Angular 应用到 Github Pages的方法
2020/07/20 Javascript
小程序实现可拖动的悬浮按钮
2020/09/07 Javascript
[51:28]EG vs Mineski 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/16 DOTA
Python使用sorted排序的方法小结
2017/07/28 Python
基于Django URL传参 FORM表单传数据 get post的用法实例
2018/05/28 Python
python实现多进程通信实例分析
2019/09/01 Python
Python使用for生成列表实现过程解析
2020/09/22 Python
python使用ctypes库调用DLL动态链接库
2020/10/22 Python
爱淘宝:淘宝网购物分享平台
2017/04/28 全球购物
护理专业的自荐信
2013/10/22 职场文书
简历的个人自我评价范文
2014/01/03 职场文书
《北大荒的秋天》教学反思
2014/04/14 职场文书
2015年办公室文员工作总结
2015/04/24 职场文书
Go Gin实现文件上传下载的示例代码
2021/04/02 Golang
openEuler 搭建java开发环境的详细过程
2022/06/10 Servers