事件代理(委托)
小段拜读几篇关于事件委托的文章以后,学习了其中的姿势(咳咳,知识。。。),于是写下这篇随笔。还是和上篇文章一样的提醒,本随笔用于小段学习的知识总结,如果你看了这篇随笔发现错误,还请指出(我怕误人子弟)-。
事件委托在JavaScript高级程序设计中定义为:利用事件冒泡,只指定一个事件处理程序,来处理一类型的所有事件。
就我理解的是:利用事件冒泡的机制,所有子节点中发生了某一类的事件以后,都会冒泡到父级,所以委托父节点来代为处理这一类事件。
现在有这么一个要求那就是,有一个ul标签中有一些li,当鼠标滑入某个li标签,相应的li标签背景变红,鼠标移出,恢复原来样式。
列表代码如下:
<ul id="parent">
<li class="child">child_1</li>
<li class="child">child_2</li>
<li class="child">child_3</li>
<li class="child">child_4</li>
</ul>
记得在学得事件委托之前,我就会写出逻辑和下面的js一样的代码:
function addEvent() {
var aChild = document.getElementsByClassName('child');
// 通过遍历所有的class为child的li元素绑定事件
for(var i = 0; i < aChild.length; i++) {
aChild[i].onmouseover = function() {
this.style.background = "orange";
};
aChild[i].onmouseout = function() {
this.style.background = '';
};
}
}
addEvent();
那样不就实现了想要的效果了吗?实际上确实是这样。那如果我们现在的增加点要求,我们可以随意的在ul中增加li标签,并且鼠标移入移出li标签的效果和前面的一样。我当时就在想,wocao这个也太简单了吧,于是噼里啪啦就写出下面的代码出来。
var parent = document.getElementById('parent');
function addEvent() {
var aChild = document.getElementsByClassName('child');
// 通过遍历所有的class为child的li元素绑定事件
for(var i = 0; i < aChild.length; i++) {
aChild[i].onmouseover = function() {
this.style.background = "orange";
};
aChild[i].onmouseout = function() {
this.style.background = '';
};
}
}
addEvent();
var newChild = document.createElement('li');
newChild.className = 'child';
newChild.innerText = 'child_5';
parent.appendChild(newChild);
/*
* 在这里再次调用addEvent原因是,
* 前面调用addEvent的时候并没有内容为child_5的节点,
* 所以只有前4个li拥有鼠标移入移出的事件监听函数。
* 后面再次调用addEvent,
* 此时内容child_5的li已经存在dom树中,
* 所以现在所有的li都有了鼠标移入移出的事件监听函数了。
*/
addEvent();
上面的代码效果是实现了,但是我们每次添加li节点以后都要对每一个li节点进行操作,这样是十分消耗内存,要知道系统分配给浏览器的内存是有限的。
下面将展示事件代理的代码:
var parent = document.getElementById('parent');
parent.addEventListener('mouseover', function(ev) {
/*
* 利用事件对象ev中的target属性可以获取到发生事件的节点,
* 再调用节点对象的nodeName属性获取到标签名,
* 注意此时获取到的标签名是大写字母,
* 调用字符串的toLowerCase()方法就可以将字符串中的字母全变成小写
*/
if(ev.target.nodeName.toLowerCase() === 'li') {
ev.target.style.background = 'orange';
}
});
// 鼠标移出和鼠标移入一样的处理方式
parent.addEventListener('mouseout', function(ev) {
if(ev.target.nodeName.toLowerCase() === 'li') {
ev.target.style.background = '';
}
});
上面的代码中我们给id为parent的ul添加事件监听,在li节点中不管鼠标移入或者移出,由于事件冒泡机制,都会被ul监听到。通过父节点的事件对象ev的target属性(ev.target
)即时获取到发生事件的子节点。
接下来我们试下父节点的监听事件是否可以监听到新增的li节点。
var parent = document.getElementById('parent');
parent.addEventListener('mouseover', function(ev) {
/*
* 利用事件对象ev中的target属性可以获取到发生事件的节点,
* 再调用节点对象的nodeName属性获取到标签名,
* 注意此时获取到的标签名是大写字母,
* 调用字符串的toLowerCase()方法就可以将字符串中的字母全变成小写
*/
if(ev.target.nodeName.toLowerCase() === 'li') {
ev.target.style.background = 'orange';
}
});
// 鼠标移出和鼠标移入一样的处理方式
parent.addEventListener('mouseout', function(ev) {
if(ev.target.nodeName.toLowerCase() === 'li') {
ev.target.style.background = '';
}
});
var newChild = document.createElement('li');
newChild.className = 'child';
newChild.innerText = 'child_5';
parent.appendChild(newChild);
从效果图我们可以看到,当鼠标滑入新增的child_5时,它的背景也变成了橘色。
小段讲得有点粗糙有什么疑惑,请留言通知我。