详解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 相关文章推荐
utf8的编码算法 转载
Dec 27 Javascript
Javascript技术技巧大全(五)
Jan 22 Javascript
jQuery登陆判断简单实现代码
Apr 21 Javascript
JS获取农历日期具体实例
Nov 14 Javascript
jQuery平滑旋转幻灯片特效代码分享
Sep 07 Javascript
JS全局变量和局部变量最新解析
Jun 24 Javascript
前端微信支付js代码
Jul 25 Javascript
JS输出空格的简单实现方法
Sep 08 Javascript
微信公众号开发 实现点击返回按钮就返回到聊天界面
Dec 15 Javascript
详解js中Number()、parseInt()和parseFloat()的区别
Dec 20 Javascript
vuex 的简单使用
Mar 22 Javascript
详解Axios统一错误处理与后置
Sep 26 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
超级简单的发送邮件程序
2006/10/09 PHP
Discuz 6.0+ 批量注册用户名
2009/09/13 PHP
PHP以json或xml格式返回请求数据的方法
2018/05/31 PHP
thinkPHP5框架闭包函数与子查询传参用法示例
2018/08/02 PHP
laravel7学习之无限级分类的最新实现方法
2020/09/30 PHP
php7连接MySQL实现简易查询程序的方法
2020/10/13 PHP
php命令行模式代码实例详解
2021/02/26 PHP
jquery $.ajax入门应用二
2008/11/19 Javascript
jQuery中ready事件用法实例
2015/01/19 Javascript
浅谈JavaScript 的执行顺序
2015/08/07 Javascript
js实现图片无缝滚动
2015/12/23 Javascript
sea.js常用的api简易文档
2016/11/15 Javascript
详谈angularjs中路由页面强制更新的问题
2017/04/24 Javascript
通过源码分析Vue的双向数据绑定详解
2017/09/24 Javascript
关于Vue背景图打包之后访问路径错误问题的解决
2017/11/03 Javascript
JavaScript实现元素滚动条到达一定位置循环追加内容
2017/12/28 Javascript
JS简单添加元素新节点的方法示例
2018/02/10 Javascript
vue使用v-if v-show页面闪烁,div闪现的解决方法
2018/10/12 Javascript
socket在egg中的使用实例代码详解
2019/05/30 Javascript
深入浅析vue全局环境变量和模式
2020/04/28 Javascript
Vue打包部署到Nginx时,css样式不生效的解决方式
2020/08/03 Javascript
python编程开发之日期操作实例分析
2015/11/13 Python
简单介绍Python中的几种数据类型
2016/01/02 Python
Python Flask框架模板操作实例分析
2019/05/03 Python
python启动应用程序和终止应用程序的方法
2019/06/28 Python
python scrapy重复执行实现代码详解
2019/12/28 Python
matplotlib 多个图像共用一个colorbar的实现示例
2020/09/10 Python
Python批量获取并保存手机号归属地和运营商的示例
2020/10/09 Python
css3中检验表单的required,focus,valid和invalid样式
2014/02/21 HTML / CSS
strstr()的简单实现
2013/09/26 面试题
人事部经理岗位职责
2014/03/07 职场文书
同学会主持词
2014/03/18 职场文书
黄金酒广告词
2014/03/21 职场文书
车间主任岗位职责
2015/02/03 职场文书
goland 恢复已更改文件的操作
2021/04/28 Golang
详解Mysql数据库平滑扩容解决高并发和大数据量问题
2022/05/25 MySQL