用事件代理、闭包(形成独立作用域)来解决循环绑定事件的bug

下面几段代码,我想实现点击按钮删除对应行的数据

<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)
        }

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值