详解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 相关文章推荐
JS 有名函数表达式全面解析
Mar 19 Javascript
Javascript无阻塞加载具体方式
Jun 28 Javascript
用JavaScript实现动画效果的方法
Jul 20 Javascript
js使用eval解析json实例与注意事项分享
Jan 18 Javascript
JQuery选择器绑定事件及修改内容的方法
Jan 23 Javascript
javascript闭包的理解
Apr 01 Javascript
完美解决jQuery符号$与其他javascript 库、框架冲突的问题
Aug 09 Javascript
巧用jQuery选择器提高写表单效率的方法
Aug 19 Javascript
Angularjs 依赖压缩及自定义过滤器写法
Feb 04 Javascript
JS实现简单获取最近7天和最近3天日期的方法
Apr 18 Javascript
微信小程序用户授权弹窗 拒绝时引导用户重新授权实现
Jul 29 Javascript
vue图片加载失败时用默认图片替换的方法
Aug 29 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
destoon之一键登录设置
2014/06/21 PHP
本地计算机无法启动Apache故障处理
2014/08/08 PHP
thinkphp四种url访问方式详解
2014/11/28 PHP
PHP添加图片水印、压缩、剪切的封装类
2015/08/17 PHP
PHP集成环境XAMPP的安装与配置
2018/11/13 PHP
YII框架学习笔记之命名空间、操作响应与视图操作示例
2019/04/30 PHP
JQuery Easyui Tree的oncheck事件实现代码
2010/05/28 Javascript
location.search在客户端获取Url参数的方法
2010/06/08 Javascript
关于JavaScript定义类和对象的几种方式
2010/11/09 Javascript
js实现在页面上弹出蒙板技巧简单实用
2013/04/16 Javascript
IE下window.onresize 多次调用与死循环bug处理方法介绍
2013/11/12 Javascript
js/jQuery简单实现选项卡功能
2014/01/02 Javascript
js用闭包遍历树状数组的方法
2014/03/19 Javascript
通过js获取上传的图片信息(临时保存路径,名称,大小)然后通过ajax传递给后端的方法
2015/10/01 Javascript
全面解析Bootstrap排版使用方法(文字样式)
2015/11/30 Javascript
实例详解JSON数据格式及json格式数据域字符串相互转换
2016/01/07 Javascript
JavaScript实现身份证验证代码
2016/02/17 Javascript
AngularJS基础 ng-model-options 指令简单示例
2016/08/02 Javascript
Vue ElementUI之Form表单验证遇到的问题
2017/08/21 Javascript
基于vue.js路由参数的实例讲解——简单易懂
2017/09/07 Javascript
详解JSON和JSONP劫持以及解决方法
2019/03/08 Javascript
js前端面试之同步与异步问题详解
2019/04/03 Javascript
Vue优化:常见会导致内存泄漏问题及优化详解
2020/08/04 Javascript
[01:10:16]DOTA2上海特级锦标赛B组资格赛#2 Fnatic VS Spirit第一局
2016/02/27 DOTA
python字典序问题实例
2014/09/26 Python
浅谈Python批处理文件夹中的txt文件
2019/03/11 Python
深度辨析Python的eval()与exec()的方法
2019/03/26 Python
Python实现SMTP邮件发送
2020/06/16 Python
澳大利亚优质的家居用品和生活方式公司:Bed Bath N’ Table
2019/04/16 全球购物
《藤野先生》教学反思
2014/02/19 职场文书
《雨点儿》教学反思
2014/04/14 职场文书
住宅使用说明书
2014/05/09 职场文书
国家奖学金获奖感言
2014/08/16 职场文书
大学运动会加油稿200字(5篇)
2014/09/27 职场文书
2019最新激励员工口号大全!
2019/06/28 职场文书
2019通用版导游词范本!
2019/08/07 职场文书