详解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动态创建div的方法
Feb 09 Javascript
JavaScript操作Cookie详解
Feb 28 Javascript
javascript制作的滑动图片菜单
May 15 Javascript
jqGrid 学习笔记整理——进阶篇(一 )
Apr 17 Javascript
第一章之初识Bootstrap
Apr 25 Javascript
微信JSAPI支付操作需要注意的细节
Jan 10 Javascript
jquery 正整数数字校验正则表达式
Jan 10 Javascript
深入理解Vue 组件之间传值
Aug 16 Javascript
微信小程序时间控件picker view使用详解
Dec 28 Javascript
详解element-ui日期时间选择器的日期格式化问题
Apr 08 Javascript
VUE组件中的 Drawer 抽屉实现代码
Aug 06 Javascript
微信小程序实现拨打电话功能的示例代码
Jun 28 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
Linux下 php5 MySQL5 Apache2 phpMyAdmin ZendOptimizer安装与配置[图文]
2008/11/18 PHP
ThinkPHP中的三大自动简介
2014/08/22 PHP
ThinkPHP结合AjaxFileUploader实现无刷新文件上传的方法
2014/10/29 PHP
PHP 双链表(SplDoublyLinkedList)简介和使用实例
2015/05/12 PHP
javascript 按回车键相应按钮提交事件
2009/11/02 Javascript
深入理解JavaScript作用域和作用域链
2011/10/21 Javascript
Ext修改GridPanel数据和字体颜色、css属性等
2014/06/13 Javascript
浅析javascript 定时器
2014/12/23 Javascript
js仿土豆网带缩略图的焦点图片切换效果实现方法
2015/02/23 Javascript
Java File类的常用方法总结
2015/03/18 Javascript
js绘制圆形和矩形的方法
2015/08/05 Javascript
Javascript原型链的原理详解
2016/01/05 Javascript
原生JS实现轮播效果+学前端的感受(防止走火入魔)
2016/08/21 Javascript
jQuery实现的多张图无缝滚动效果【测试可用】
2016/09/12 Javascript
JS中关于事件处理函数名后面是否带括号的问题
2016/11/16 Javascript
Vue.js tab实现选项卡切换
2017/05/16 Javascript
Vue.js实现分页查询功能
2020/11/15 Javascript
vue拦截器实现统一token,并兼容IE9验证功能
2018/04/26 Javascript
webpack常用构建优化策略小结
2019/11/21 Javascript
node.js Promise对象的使用方法实例分析
2019/12/26 Javascript
vue实现在线学生录入系统
2020/05/30 Javascript
js面向对象方式实现拖拽效果
2021/03/03 Javascript
[50:04]DOTA2上海特级锦标赛D组小组赛#2 Liquid VS VP第二局
2016/02/28 DOTA
[03:55]TI9战队采访——TNC Predator
2019/08/22 DOTA
python操作摄像头截图实现远程监控的例子
2014/03/25 Python
python魔法方法-属性访问控制详解
2016/07/25 Python
python实现教务管理系统
2018/03/12 Python
python开启debug模式的方法
2019/06/27 Python
python 命令行传入参数实现解析
2019/08/30 Python
Pandas聚合运算和分组运算的实现示例
2019/10/17 Python
对Keras中predict()方法和predict_classes()方法的区别说明
2020/06/09 Python
松本清官方海外旗舰店:日本最大的药妆连锁店
2017/11/21 全球购物
学雷锋活动总结报告
2014/06/26 职场文书
党员民主生活会材料
2014/12/15 职场文书
apache ftpserver搭建ftp服务器
2022/05/20 Servers
Win11 Build 25179预览版发布(附更新内容+ISO官方镜像下载)
2022/08/14 数码科技