for循环添加事件监听
在写某个需求时,想要做点击按钮动态添加样式表示选中的效果,在写
js
代码的时候遇到了疑问点。
页面如上所示,头部的三个按钮在点击后永远只会对最后一个按钮进行样式添加或移除。
var chooseArea = document.getElementsByClassName("choose-area")[0];
for(var i=0;i<chooseArea.children.length;i++){
console.log(chooseArea.children);
var child = chooseArea.children[i];
child.onclick=(function(){
console.log(child);
if(child.className=="")
child.className="active";
})
}
此处先拿出主要的代码(此时的已经是不完整+乱改版本),部分打印代码为调试时编写。
查了一下百度,大概了解到是 onclick
或是添加事件监听一类都不是即时运行。而内部会调用外部的变量,在 click
事件的函数运行时,i
早已自增至最大值,所以永远指向最后一个元素。此处的解决方法是闭包。(我终于知道闭包的作用了…)
修改后的代码(依然有待改进,时间未定):
var chooseArea = document.getElementsByClassName("choose-area")[0];
for(var i=0;i<chooseArea.children.length;i++){
console.log(chooseArea.children);
var child = chooseArea.children[i];
(function run(child,i){
child.onclick = function () {
if (child.className == "")
child.className = "active";
else {
child.className="";
}
for(var j=0;j<chooseArea.children.length;j++){
if(i==j){
continue;
}
else{
chooseArea.children[j].className="";
}
}
};
})(child,i);
闭包都解决不了的我可真是个菜鸡…(lll¬ω¬)
2020 年的回顾
这篇大概是最让我无奈的,现在我已经完全清楚笔记中记录的问题了。
一个很普遍简单的需求,多个按钮,监听按钮的点击事件来切换选中状态。
我们选择遍历按钮的 dom
类数组,如果仅仅采用 ES5
来完成,确实会遇到问题。
原因在于 var
声明的局限性, var
不存在块级作用域,在 for
循环内部声明的所有变量的作用域都是高于块级的,所有块级内部共享同样的变量,所以最后我们能找到的 i
是循环结束的值,所有按钮的点击事件都是触发最后一个按钮的样式变化。
此处的解决方法有:
- 使用
let
声明块级作用域的变量。 - 调用立即执行函数形成闭包,也就是形成函数作用域来保存每一个
i
。