通过DOM JavaScript绑定按钮事件异常:参数始终为最后一项
问题原因与解决方案
问题根源
这是JavaScript闭包与var作用域的经典陷阱:
- 循环中用
var声明的i和filename属于函数级作用域,整个循环共享同一个变量引用 - 点击事件的回调函数是闭包,它不会立即执行,而是在触发点击时才读取
filename的值——此时循环已结束,filename早已被赋值为最后一个元素的name(即"4")
解决方案
方案1:用let替代var(推荐)
let是ES6引入的块级作用域变量,每次循环都会创建独立的变量实例,回调函数会绑定当前迭代的变量值:
var jsonData = JSON.parse(jsonstring); let element = document.getElementById("fi"); while (element.firstChild) { element.removeChild(element.firstChild); } // 将循环的var i改为let i for (let i = 0; i < jsonData.files.length; i++) { let innerNameDiv = document.createElement('div'); innerNameDiv.className = 'ccl ccu'; innerNameDiv.style.float = "left"; innerNameDiv.style.width = "45%"; innerNameDiv.innerHTML = " " + jsonData.files[i].name; let innerFileSizeDiv = document.createElement('div'); innerFileSizeDiv.className = 'cct ccu'; innerFileSizeDiv.style.float = "left"; innerFileSizeDiv.style.width = "30%"; innerFileSizeDiv.textContent = jsonData.files[i].filesize; let innerButtonDiv = document.createElement('div'); innerButtonDiv.className = 'ccr ccu'; innerButtonDiv.style.float = "left"; innerButtonDiv.style.width = "20%"; let innerButtonDelete = document.createElement("button"); innerButtonDelete.className = "b"; // 原代码的class属性错误,需改为className innerButtonDelete.innerText = "Del"; // 用let声明filename,绑定当前迭代的文件名 let filename = jsonData.files[i].name; innerButtonDelete.addEventListener('click', () => { deleteFile(filename) }); innerButtonDiv.appendChild(innerButtonDelete); element.appendChild(innerNameDiv); element.appendChild(innerFileSizeDiv); element.appendChild(innerButtonDiv); console.log(jsonData.files[i].name + ":" + jsonData.files[i].filesize); }
方案2:用立即执行函数(IIFE)包裹回调
通过IIFE创建独立作用域,保存当前迭代的filename值:
// 其他循环代码与原代码一致,仅修改事件绑定部分 var filename = jsonData.files[i].name; innerButtonDelete.addEventListener('click', (function(name) { return function() { deleteFile(name); } })(filename));
方案3:利用元素dataset存储数据
把文件名存在按钮的自定义数据属性中,点击时从元素自身读取:
// 其他循环代码与原代码一致,仅修改事件绑定部分 innerButtonDelete.dataset.filename = jsonData.files[i].name; innerButtonDelete.addEventListener('click', function() { deleteFile(this.dataset.filename); });
额外修正
原代码中innerButtonDelete.class = "b"是错误写法,DOM元素设置类名需使用className属性,上述方案已统一修正。
内容的提问来源于stack exchange,提问作者test1
相关产品推荐
相关产品推荐

