马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 不二如是 于 2019-1-11 17:32 编辑
很多鱼油关于JS的问题会涉及到事件委托,在0 2 3 2 ★ JS执行机制大作战 中也涉及到了。
还是有必要仔细围绕这个东东,好好说一下。
事件委托的定义:
利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
何为事件冒泡(event bubbling)呢?
这个概念是有微软提出滴,事件冒泡可以形象地比喻为:
也就是说:
事件会从最内层的元素开始发生,一直向上传播,直到document对象。
因此在事件冒泡的概念下在 p 元素上发生 click 事件的顺序应该是:p -> div -> body -> html -> document
大概了解到这一层面就够了,有机会关于“事件捕获和冒泡”,好好讲一下~
继续回到事件委托,在 js 中添加到页面上的事件处理程序的个数直接影响到网页的运行性能。
因为每个事件处理函数都是一个对象,是对象就会占用内存,而内存中对象越多,导致的结果就是性能越差。
而访问 dom 的次数越多,就会引起结构的重绘或者重排的次数也随之增多,会延迟整个页面的交互就绪时间。
这样对于在页面进行处理过后新增的 dom 元素,运用事件委托可以为新增的dom元素一并增减事件处理程序。
理论就是上面这样啦,进入敲代码环节。
我们实现一个p元素单击时,背景变绿的交互,创建基本代码:<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件委托</title>
</head>
<body>
<div id="nodes">
<P class="node_p">第一个p</P>
<P class="node_p">第二个p</P>
<P class="node_p">第三个p</P>
<div id="childDiv">
<p>这是子集菜单</p>
<div>我是子集的div
<p>我是子集的p</p>
</div>
</div>
</div>
<button>单击新增一个p元素</button>
</body>
</html>
接下来实现 JS 代码部分,我们先不要用事件委托,实现:
获取页面中所有的 p 元素然后用 for 循环遍历给每一个元素增加事件处理函数
创建JS:<script>
var nodes = document.getElementById("nodes");
var ps = document.getElementsByTagName("p");
console.log(ps);
var btn = document.getElementsByTagName("button")[0];
var inner = 33;
btn.onclick = function() {
inner++;
var p = document.createElement("p");
p.innerHTML = inner + "新增的p标签啊";
nodes.appendChild(p);
console.log(ps);
};
for (var i= 0;i<ps.length;i++){
ps[i].onclick= function(){
this.style.background = 'green'
}
}
</script>
然后单击p元素会看到:
原来存在的 p 元素单击后背景色正确变绿了,而新增加的p元素没有成功!
怎么肥四?
可以理解为浏览器的一种优化,如果每一个未经授权的新增元素获得所有现存的方法岂不乱套?
需要为新增的元素增加一个事件处理函数。
新增一个 myAddEvent() : var nodes = document.getElementById("nodes");
var ps = document.getElementsByTagName("p");
console.log(ps);
var btn = document.getElementsByTagName("button")[0];
var inner = 33;
btn.onclick = function() {
inner++;
var p = document.createElement("p");
p.innerHTML = inner + "新增的p标签啊";
nodes.appendChild(p);
// 将新dom元素增加到页面后在执行
myAddEvent();
console.log(ps);
};
// 增加事件处理函数,包裹for循环
function myAddEvent(){
for (var i= 0;i<ps.length;i++){
ps[i].onclick= function(){
this.style.background = 'green'
}
}
}
搞定啦!
这时候虽然解决了为新增dom元素增加事件处理函数的问题,但是仔细考虑挥会发现:
究其原因就是又增加了一个事件处理函数(对象),又一次占用了内存。
所以,这个时候就会用到事件委托,这时候事件委托的优势也有所体现出来: var nodes = document.getElementById("nodes");
var ps = document.getElementsByTagName("p");
console.log(ps);
var btn = document.getElementsByTagName("button")[0];
var inner = 33;
btn.onclick = function() {
inner++;
var p = document.createElement("p");
p.innerHTML = inner + "新增的p标签啊";
nodes.appendChild(p);
console.log(ps);
};
// 事件委托
nodes.onclick = function(e){
var ev = e || window.event;
var target = ev.target;
//这里要判被处理元素节点的名字
if(target.nodeName.toLowerCase() == 'p'){
target.style.background = 'green'
}
}
通过 if 我们判被处理元素节点的名字。
也可以增加相应的判断条件: target.nodeName.toLowerCase() == 'p'||target.nodeName.toLowerCase() == 'span'
但是要注意不要使用父级元素的名称,因为再点击子元素之间的空气的时候,由于事件冒泡他会给父级元素也增加相应的事件处理函数。
因为返回的节点名称一般都是大写,所以这时要用 toLowerCase() 处理一下。
如果喜欢,别忘了评分 :
这位鱼油,如果喜欢本系列Js帖子,请订阅 专辑☞( 传送门)( 不喜欢更要订阅 )
|