循环创建带不同onclick事件的按钮时URL跳转异常问题
问题原因
你遇到的是变量作用域导致的经典问题:用var声明的b属于函数级作用域,循环结束后它会保留最后一次循环的索引值,所有点击事件触发时都会读取这个最终值,因此全部指向最后一个URL。另外原代码里的result是数组,但循环调用了result["buttons"],这会导致读取失败,需要先修正数据结构(如果API返回的是带buttons属性的对象)或者直接遍历数组。
解决方案
以下是几种原生JS的可行方案,按需选择:
方案1:用let替代var(ES6+,主流浏览器支持)
let会为每次循环创建独立的作用域,确保点击事件能捕获当前循环的正确值:
// 修正数据结构(如果API返回的是带buttons属性的对象) var result = { buttons: ["foo.com", "bar.com", "baz.com"] }; var buttDiv = document.getElementById("buttonsHeader"); // 把var改成let for(let b in result["buttons"]) { var nButton = document.createElement("button"); nButton.innerHTML = result["buttons"][b]; nButton.addEventListener("click", function(){ buttonRedir(result["buttons"][b]); }); buttDiv.appendChild(nButton); } function buttonRedir(link) { console.log(link); window.open( "https://" + link, '_blank' ); }
方案2:立即执行函数(IIFE)兼容旧浏览器
如果需要兼容不支持ES6的旧环境,用IIFE将当前URL封装到独立作用域:
var result = { buttons: ["foo.com", "bar.com", "baz.com"] }; var buttDiv = document.getElementById("buttonsHeader"); for(var b in result["buttons"]) { var nButton = document.createElement("button"); var currentLink = result["buttons"][b]; nButton.innerHTML = currentLink; // 用IIFE捕获当前URL nButton.addEventListener("click", (function(link){ return function() { buttonRedir(link); }; })(currentLink)); buttDiv.appendChild(nButton); } function buttonRedir(link) { console.log(link); window.open( "https://" + link, '_blank' ); }
方案3:自定义数据属性(直观易维护)
将URL存储在按钮的data-*属性中,点击时直接读取当前按钮的属性值:
var result = { buttons: ["foo.com", "bar.com", "baz.com"] }; var buttDiv = document.getElementById("buttonsHeader"); for(var b in result["buttons"]) { var nButton = document.createElement("button"); var currentLink = result["buttons"][b]; nButton.innerHTML = currentLink; // 绑定自定义数据属性 nButton.dataset.link = currentLink; nButton.addEventListener("click", function(){ buttonRedir(this.dataset.link); }); buttDiv.appendChild(nButton); } function buttonRedir(link) { console.log(link); window.open( "https://" + link, '_blank' ); }
补充:如果API返回纯数组
如果API直接返回URL数组(比如var result = ["foo.com", "bar.com", "baz.com"]),推荐用forEach遍历,避免for...in可能带来的原型属性遍历问题:
var result = ["foo.com", "bar.com", "baz.com"]; var buttDiv = document.getElementById("buttonsHeader"); result.forEach(function(link) { var nButton = document.createElement("button"); nButton.innerHTML = link; nButton.addEventListener("click", function(){ buttonRedir(link); }); buttDiv.appendChild(nButton); }); function buttonRedir(link) { console.log(link); window.open( "https://" + link, '_blank' ); }
内容的提问来源于stack exchange,提问作者MattW
相关产品推荐
相关产品推荐

