前沿
这段时间在做一个论坛网站,由于文章需要动态添加在列表后面,添加之后就发现一个问题。之前在列表中用for给列表元素addEventListener了一些监听事件,在后面动态添加的元素中没有生效。这是因为,在后面动态加入的dom中没有执行addEventListener事件,当然我们也可以在动态添加完后再给他们添加一次监听事件,这有可能会出现一个问题,就是前面的元素可能会重复添加监听事件,导致执行多次事件处理。这不断操作DOM,也会造成巨大的性能损耗。所以这里我们引用了----事件委托。
事件委托
将子元素的事情委托给父元素,然后通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件源的某种特性来执行相应的业务逻辑。
处理动态添加事件
如果我们按照正常的for循环添加事件绑定,如下 代码片
.
// An highlighted block
<body>
<ul>
<li>s1</li>
<li>s2</li>
<li>s3</li>
<li>s4</li>
</ul>
<button id='add'>添加属性</button>
<script>
(function(){
let lis = document.getElementsByTagName('li');
let addBtn = document.getElementById("add");
let ul = document.getElementsByTagName('ul');
for(let i=0,len=lis.length;i<len;i++){
lis[i].addEventListener('click',()=>{
alert(`你点到我了!${lis[i].innerText}`);
})
}
addBtn.addEventListener('click',()=>{
let li = document.createElement('li');
li.innerHTML = "新增元素";
ul[0].appendChild(li);
})
})()
</script>
</body>
这里就会出现一个问题,前面原先有的li点击是可以触发处理事件,点击添加按钮后动态添加的li却木有绑定上点击事件。
这时我们可以将子元素的事件委托给父元素去绑定执行,这样我们就无需关心子元素的修改。我们将上诉代码修改为
<body>
<ul>
<li>s1</li>
<li>s2</li>
<li>s3</li>
<li>s4</li>
</ul>
<button id='add'>添加属性</button>
<script>
(function(){
let addBtn = document.getElementById("add");
let ul = document.getElementsByTagName('ul');
ul[0].addEventListener('click',(e)=>{
e = e || window.event;
let tar = e.target || e.srcElement;
if(tar.nodeName.toLowerCase()=='li'){
alert(`你点到我了!${tar.innerText}`);
}
})
addBtn.addEventListener('click',()=>{
let li = document.createElement('li');
li.innerHTML = "新增元素";
ul[0].appendChild(li);
})
})()
</script>
</body>
这样我们就无需关系ul后面添加多少个li,都依旧能触发相应处理。
处理内存外泄问题
像老版本的IE浏览器,其用的是计数式垃圾回收机制,使得一些对Dom元素引用没有显性清除的数据会遗留在内存中,除非关闭浏览器,否则无法清除。例如
<div id="btn_container">
<button id="btn">demo</button>
</div>
var g = function(id){return document.getElementById(id);}
g('btn').onclick=function(){
g('btn_container').innerHTML = "触发了事件!";
}
这时点击btn时,就会触发事件处理把btn按钮给覆盖,但是其click事件没有被清除,就会泄露到内存中,当然可以将其设为null
g(‘btn’).οnclick=null;更好的办法就是使用委托模式。
g('btn_container').onclick=function(e){
//获取触发事件
var target = e && e.target || window.event.srcElement;
//判断触发事件元素id是否为btn
if(target.id==='btn'){
//重置父元素内容
g('btn_container').innerHTML = '触发了事件';
}
}
当然委托模式不仅仅有这些作用,在后端可以处理事件分发的问题,将多个请求打包成一个节约多次请求对时间的开销。