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);
});

js事件代理(委托)的更多相关文章

  1. JS 事件代理

    事件处理器:onclick.onmouseover.... 在传统的事件处理中,你需要为每一个元素添加或者是删除事件处理器.然而,事件处理器将有可能导致内存泄露或者是性能下降——你用得越多这种风险就越 ...

  2. JavaScript中的事件代理/委托

    事件委托在JS高级程序设计中的定义为"利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件" 如何理解上面的这句话呢,在网上,大牛们一般都使用收快递这个例子来解释的, ...

  3. js事件代理

    需要注意的blog:http://blog.csdn.net/majian_1987/article/details/8591385 一篇博客看懂  http://blog.csdn.net/maji ...

  4. 封装Js事件代理方法

    // 封装事件代理 function delegateEvent(element, tag, event, listener) { // 判断是否支持addEventlistener if(eleme ...

  5. 关于JS事件冒泡与JS事件代理(事件委托)

    连接:https://blog.csdn.net/supercoooooder/article/details/52190100 核心代码: <ul id="parentUl" ...

  6. js中事件代理(委托)

    var oul = document.getElementById(‘uli’); oul.onclick = function(e) { e = e || window.event; var tar ...

  7. JS事件流(W3C与IE区别)

    一.JS事件的3个阶段:捕获.目标.冒泡,低版本IE不支持捕获阶段: 二.在浏览器解析事件的时候,有两种触发方式:一种叫做Bubbling(冒泡),另外一种叫做Capturing(捕获). 冒泡的方式 ...

  8. js中的事件委托或是事件代理详解

    起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...

  9. JS中事件代理与委托

    在javasript中delegate这个词经常出现,看字面的意思,代理.委托.那么它究竟在什么样的情况下使用?它的原理又是什么?在各种框架中,也经常能看到delegate相关的接口.这些接口又有什么 ...

随机推荐

  1. Linux下hostname与hosts

    参考:http://wp.fungo.me/linux/what-the-hell-is-hostname.html hostname 就是机器名,内核中的一个变量,可临时修改也可以永久修改 /etc ...

  2. java8

    1:Scanner的使用(了解) (1)在JDK5以后出现的用于键盘录入数据的类. (2)构造方法: A:讲解了System.in这个东西. 它其实是标准的输入流,对应于键盘录入 B:构造方法 Inp ...

  3. Linux下安装cmake

    cmake是一个跨平台的编译工具,特点是语句简单,编译高效,相对于原有的automake更为高效,接下来说明在Linux下安装cmake工具的过程 首先去cmake官网下载cmake安装包,下载界面网 ...

  4. .NET 委托

    委托类型定义      C#编译器处理委托时,先自动产生一个派生自System.MulticastDelegate的密封类.这个类与它的基类System.Delegate一起为委托提供必要的基础设施, ...

  5. HTML5本地数据库(SQLite)示例

    本文转载自http://blog.sina.com.cn/s/blog_641cf27f01016pm5.html 按照国内一HTML5先行者的例子仿写了一个用HTML5 API来操作本地SQLite ...

  6. Android开发之---AIDL

    在Android开发中,有时会用到多进程通信,这时,可选的方案为: 1. Bundle    :四大组件之间的进程间通信 2. 文件共享   :适合无并发情景 3. Messager : 低并发的一对 ...

  7. Gerrit管理帐号

    文档 Gerrit服务器启动后,网站上有一个Documentation链接.点击后会看到四个选项: index,searching,uploading和access control 文档内容很多,不必 ...

  8. HTML5离线Web应用实战:五步创建成功

    [IT168 技术]HTML5近十年来发展得如火如荼,在HTML 5平台上,视频,音频,图象,动画,以及同电脑的交互都被标准化.HTML功能越来越丰富,支持图片上传拖拽.支持localstorage. ...

  9. UDP协议开发

    UDP是用户数据报协议(User Datagram Protocol,UDP)的简称,其主要作用是将网络数据流量压缩成数据报形式,提供面向事务的简单信息传送服务.与TCP协议不同,UDP协议直接利用I ...

  10. Delphi 各版本新特性功能网址收集

    Delphi XE2 三个新功能介绍举例_西西软件资讯  http://www.cr173.com/html/13179_1.html delphi 2007新功能简介-davidxueer-Chin ...