下面几段代码,我想实现点击按钮删除对应行的数据
<ul>
<li>Lorem.<button>删除</button></li>
<li>Commodi.<button>删除</button></li>
<li>Tenetur!<button>删除</button></li>
<li>Corrupti?<button>删除</button></li>
<li>Obcaecati.<button>删除</button></li>
<li>Praesentium.<button>删除</button></li>
<li>Vero?<button>删除</button></li>
<li>Voluptates.<button>删除</button></li>
<li>Perferendis?<button>删除</button></li>
<li>Aliquam!<button>删除</button></li>
</ul>
此时很多人会想到循环绑定事件,每次点击删除此行
var btns = document.querySelectorAll('button');
for(var i = 0;i < btns.length;i++){
btns[i].onclick = function(){
btns[i].parentElement.remove();
}
}
但是当你去运行时就会发现只删除最后一行,这里可以做个验证,当我们点击按钮时让控制台打印对应的行数
var btns = document.querySelectorAll('button');
for(var i = 0;i < btns.length;i++){
btns[i].onclick = function(){
console.log(i);
// btns[i].parentElement.remove();
}
}
运行点击删除按钮,我们会发现控制台每回都是打印10
也就是说无论如何我们点击哪一个按钮,我们永远都是输出的最后一行。这是为什么呢?
因为事件的绑定和事件的执行是两件事,当我们能触发点击事件,就说明事件已经绑定完了,那么此时i就变为了最后一个数,也就是10。那么我们怎么解决呢?
最简单的办法两种办法(this let):
1.把循环里的的var改成let:因为每一次循环let都形成了一个独立的作用域(这里不作详细说明,想要了解的大佬可以查阅资料,有机会的话我也会专门发博客来讲解一下)
var btns = document.querySelectorAll('button');
for(let i = 0;i < btns.length;i++){
btns[i].onclick = function(){
btns[i].parentElement.remove();
}
}
2.用this:这里要注意this的指向问题(此处不做详细说明),这里的this指向调用这个函数的对象(即btns[i])
var btns = document.querySelectorAll('button');
for(var i = 0;i < btns.length;i++){
btns[i].onclick = function(){
this.parentElement.remove();
}
}
当然,此时可以用事件代理,也就是说我们用ul来处理li
这就是一个非常简单的事件代理的案例,将事件委托给父元素或者祖辈元素
var ul = document.getElementsByTagName('ul')[0];
ul.onclick = function(e){
// 判断当前点击的目标元素是不是button,只有点击的元素时button 才能做删除操作
if(e.target.nodeName === 'BUTTON'){
// 一个元素的父节点一定是一个元素节点
e.target.parentElement.remove();
}
}
也可以用闭包来解决:逻辑大概为每次循环立即执行函数( (function fn(){}() )都形成独立作用域,然后每次循环都将i值传参,使index的值为i
var btns = document.querySelectorAll('button');
for(var i = 0;i < btns.length;i++){
(function fn(index){
btns[index].onclick = function(){
btns[index].parentElement.remove();
// this.parentElement.remove();
}
})(i)
}