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. OF寄存器的判断

      1000 0000 ADD  1100 0000  10 1:符号位是否有进位  有则为1 2:最高有效数值位是否向符号位产生进位  有则为1 1 XOR 0=1所以PF=1

  2. SpringMVC基本配置

    1 springmvc 框架 1.1  回顾mvc结构 1.2springmvc的介绍 是一个基于mvc的web层框架,使用spring的一个子项目 在使用springmvc的时候,不用单独和spri ...

  3. python之OS模块详解

    python之OS模块详解 ^_^,步入第二个模块世界----->OS 常见函数列表 os.sep:取代操作系统特定的路径分隔符 os.name:指示你正在使用的工作平台.比如对于Windows ...

  4. iOS之initialize与load

    initialize和load 这两个方法都是是什么时候调用的呢?都有着什么样的作用,下面看看吧! initialize +(void)initialize{ } 什么时候调用:当第一次使用这个类的时 ...

  5. iOS之九宫格图片

    照片 现在人们的生活越来越丰富了,很多美好的瞬间都定格在一张张色彩绚丽的照片上,或许把照片珍藏在相册里,或许通过社交软件分享给亲朋好友.那社交软件上的照片是以什么形式展现的呢?那么接下来就要说到九宫格 ...

  6. http协议和web应用有状态和无状态浅析

    http协议和web应用有状态和无状态浅析 (2013-10-14 10:38:06) 转载▼ 标签: it   我们通常说的web应用程序的无状态性的含义是什么呢? 直观的说,“每次的请求都是独立的 ...

  7. 继承Thread类

    Thread类在包java.lang中,从这个类中实例化的对象代表线程,启动一个新线程需要建立Thread实例,Thread类中常用的两个构造方法如下: (1)public Thread(String ...

  8. MyBatis学习(四)MyBatis和Spring整合

    MyBatis和Spring整合 思路 1.让spring管理SqlSessionFactory 2.让spring管理mapper对象和dao. 使用spring和mybatis整合开发mapper ...

  9. 【BZOJ】3495: PA2010 Riddle

    题意 \(n(1 \le n \le 1000000)\)个城市,\(k(1 \le k \le n)\)个国家,\(m(1 \le m \le 1000000)\)条边.要求每个国家有且仅有一个首都 ...

  10. SQL中对于两个不同的表中的属性取差集except运算

    SQL中对两个集合取差集运算,使用except关键字,语法格式如下: SELECT column_name(s) FROM table_name1 EXCEPT SELECT column_name( ...