为3个按钮动态调用getElementById绑定事件失效问题求助
按钮点击事件动态绑定出错?我来帮你排查!
先贴出你提供的代码,方便咱们一起分析:
<html> <head></head> <body> <button id="btn-0">Button 1!</button> <button id="btn-1">Button 2!</button> <button id="btn-2">Button 3!</button> <script type="text/javascript"> var prizes = ['A Unicorn!', 'A Hug!', 'Fresh Laundry!']; for (var i = 0; i < prizes.length; i++) { // for each of our buttons, when the user clicks it... document.getElementById('btn-' + (i-1)).onclick = function() { alert(prizes[i-1]); }; } </script> </body> </html>
咱们一步步拆解问题:
问题1:索引写错了,导致按钮绑定不全甚至找不到元素
你的循环从i=0开始,第一次循环时i-1等于-1,这时候你要找的元素是btn--1——这显然不存在啊!后面两次循环i=1和i=2时,i-1是0和1,也就是只能绑定到btn-0和btn-1,最后一个按钮btn-2完全没被绑定事件,点击肯定没反应。
问题2:var变量的闭包陷阱(最致命的问题)
就算你把索引改对了,用var声明的i会在整个函数作用域里共享,循环结束后i的值会变成3(因为循环条件是i < 3,最后一次迭代后i递增到3才退出循环)。当你点击按钮时,回调函数里的i引用的是这个已经变成3的全局变量,这时候所有按钮点击都会弹出同一个内容(甚至可能出现undefined),这显然不是你想要的效果——因为所有回调函数共享同一个i变量。
现在给你几个可行的解决方案:
方案1:用let解决闭包问题,同时修正索引
ES6的let在循环里会为每个迭代创建独立的作用域,每个回调函数都会绑定当前迭代的i值,完美解决闭包陷阱。同时把索引改成直接用i,对应按钮的btn-0、btn-1、btn-2:
<html> <head></head> <body> <button id="btn-0">Button 1!</button> <button id="btn-1">Button 2!</button> <button id="btn-2">Button 3!</button> <script type="text/javascript"> var prizes = ['A Unicorn!', 'A Hug!', 'Fresh Laundry!']; for (let i = 0; i < prizes.length; i++) { document.getElementById('btn-' + i).onclick = function() { alert(prizes[i]); }; } </script> </body> </html>
方案2:用立即执行函数(IIFE)兼容旧浏览器
如果需要支持不兼容ES6的老浏览器,可以用立即执行函数把每次循环的i值封装到独立作用域里,避免共享变量的问题:
<html> <head></head> <body> <button id="btn-0">Button 1!</button> <button id="btn-1">Button 2!</button> <button id="btn-2">Button 3!</button> <script type="text/javascript"> var prizes = ['A Unicorn!', 'A Hug!', 'Fresh Laundry!']; for (var i = 0; i < prizes.length; i++) { // 用IIFE捕获当前迭代的i值 (function(currentIndex) { document.getElementById('btn-' + currentIndex).onclick = function() { alert(prizes[currentIndex]); }; })(i); } </script> </body> </html>
方案3:用事件委托更高效(推荐)
如果以后按钮数量会变,或者是动态生成的,事件委托是更优的选择——不需要给每个按钮单独绑定事件,只需要绑定到它们的父容器上:
<html> <head></head> <body> <!-- 给按钮加个父容器 --> <div id="button-group"> <button id="btn-0">Button 1!</button> <button id="btn-1">Button 2!</button> <button id="btn-2">Button 3!</button> </div> <script type="text/javascript"> var prizes = ['A Unicorn!', 'A Hug!', 'Fresh Laundry!']; // 把事件绑定到父容器 document.getElementById('button-group').addEventListener('click', function(e) { // 判断点击的是按钮元素 if(e.target.tagName === 'BUTTON') { // 从按钮id里提取索引 var index = parseInt(e.target.id.split('-')[1]); alert(prizes[index]); } }); </script> </body> </html>
内容的提问来源于stack exchange,提问作者Deadpool
相关产品推荐
相关产品推荐

