先说说为什么要讨论这个话题,工作阅历多一点的WEB前端工程师都知道,我们经常需要对页面上大量具有某种共性的节点绑定同样的一个事件处理器,传统的方式是将这些节点获取为一个对象集合,然后对每个集合绑定一次事件,很显然,当集合只有两三个元素的时候,我们这样做无所谓。但当数量达到几十甚至几百个的时候怎么办呢?使用这种遍历对象集依次进行绑定的方式势必会造成很蛋疼的性能问题。特别是在那些低端浏览器上。他们拥有着性能非常可悲的javascript解释引擎,这给我们那些要求页面体验和流畅度的工程师们带来的无疑是很沉重的打击。
今天就给大家介绍一个名词“事件委托”,高手就不要在这里嘲笑我了,我这都是写给新手看看的。所谓“事件委托”,如同名字一样,是我们将这个集合中每个元素的事件,委托给某一个元素来处理,这样我们只需要绑定一个元素的某个事件,就可以达到我们所需要的效果。为了大家能够清楚的了解其中的原理,我在这里不进行事件绑定的相关讲解,绑定的时候只使用DOM0级方法,兼容所有浏览器即可。
不废话,且看代码,原理在代码的注释中写得很详细
你也可以直接查看文章后面的无注释代码
//事件委托
//这里我们创建bind函数,给了四个参数
//obj,需要绑定事件的节点,
//tar,在obj容器中的需要批量绑定事件的目标元素标签名
//evName,需要绑定的事件名称
//fn,需要为目标元素绑定的事件处理函数
var bind = function(obj,tar,evName,fn){
obj['on'+evName] = function(event){
var e = event||window.event,
//首先对event做兼容,window.event主要用于兼容IE
target = e.target|| e.srcElement,
//对target做兼容,e.srcElement主要用于兼容IE,这里获取的是触发事件的目标元素
bool = true;
//设置一个bool型变量,后面会用到
if(target != obj){
//这一步比较重要,判断target是否和obj相等,目的在于区别触发事件的元素是否为绑定事件的元素本身,如果是,很明显不符合要求,因为我们要触发事件的对象应该是绑定元素内的某一些元素。
(function(){
//这里做了一个匿名函数,主要用于循环判断我们触发事件的元素(target)是否在我们的目标元素(tar)内
if(target.tagName != tar.toUpperCase()){
//判断触发事件的元素标签名(tagName)是否和我们提供的目标元素的标签名(tar)相等,
target = target.parentNode;
//如果不是目标元素,则寻找触发事件元素的父元素,并修改target指向这个父元素
if(target!=obj){
//判断修改过后的target是否为我们绑定事件的元素
arguments.callee();
//如果不是我们绑定事件的元素,则重新运行这个匿名函数
}else{
bool = false;
//如果是这个我们绑定事件的元素,则停止重新运行这个匿名函数,因为这代表我们的循环已经达到了绑定事件的元素,再往外查找已经没有必要了,这里将bool设置为false
}
}
})();
//这里的空括号用于执行匿名函数
if(bool){fn(target);}
//判断是bool是否为true,如果为true,表示我们触发事件的元素在目标元素(tar)内,运行事件处理函数,并传入目标元素对象,如果为false,则相反。
}
}
}
看完上面的代码,难免大家有点迷糊,其实这里面有两个要点,我来总结一下,大家便明白了
第一、通过给obj容器绑定事件,然后通过target来得到触发这个事件的对象
第二、利用里面的匿名函数进行循环,检测触发事件的元素是否为我们想要委托的对象
附上逻辑图
无注释版本代码
var bind = function(obj,tar,evName,fn){
obj['on'+evName] = function(event){
var e = event||window.event,
target = e.target|| e.srcElement,
bool = true;
if(target != obj){
(function(){
if(target.tagName != tar.toUpperCase()){
target = target.parentNode;
if(target!=obj){
arguments.callee();
}else{
bool = false;
}
}
})();
if(bool){fn(target);}
}
}
}
本文来自:webzhan的前端技术博客