如何为动态创建的表格元素绑定带正确索引参数的onclick事件
解决循环绑定事件时参数取最后一个值的问题
这个问题我太熟了!你遇到的是JavaScript闭包作用域的经典陷阱:
- 直接写
PlayMusic(i,j)会立即执行,因为你是在调用函数,而不是把函数引用传给addEventListener; - 用
function(){}包裹后,点击时i和j总是取循环最后一个值,是因为你用var声明的循环变量属于函数级作用域,所有事件处理函数共享同一个i、j的引用——等循环结束,这两个变量已经变成循环的最终值了。
下面给你几种靠谱的解决办法,按推荐程度排序:
1. 用ES6的let替代var(最简洁)
let有块级作用域,每次循环都会创建新的变量绑定,每个事件处理函数会捕获当前循环的i和j:
var thetable = document.getElementById("mustable"); // 把循环里的var改成let for(let i=0; i<fullists.length-1; i++) { fullists[i][2] = []; fullists[i][3] = []; for(let j=0; j<fullists[i][1].length; j++) { var row = thetable.insertRow(); fullists[i][2][j] = row.insertCell(); fullists[i][2][j].className = "musentry"; var header = fullists[i][0].substring(0,fullists[i][0].lastIndexOf(".")); var title = fullists[i][1][j][1] || fullists[i][1][j][0].substring(fullists[i][1][j][0].lastIndexOf("/")); fullists[i][2][j].innerHTML = header + "<br /><b>" + title + "</b>"; // 直接绑定即可,此时i和j是当前循环的独立值 fullists[i][2][j].addEventListener("click",function() { PlayMusic(i,j); },false); fullists[i][3][j] = 0; } }
注:现在主流浏览器都支持ES6,这个方案优先用,代码最干净。
2. 用立即执行函数(IIFE)捕获当前值(兼容老环境)
如果需要兼容不支持ES6的老浏览器,可以用IIFE把当前的i和j封装成局部变量,让每个事件处理函数持有独立的引用:
var thetable = document.getElementById("mustable"); for(var i=0; i<fullists.length-1; i++) { fullists[i][2] = []; fullists[i][3] = []; for(var j=0; j<fullists[i][1].length; j++) { var row = thetable.insertRow(); fullists[i][2][j] = row.insertCell(); fullists[i][2][j].className = "musentry"; var header = fullists[i][0].substring(0,fullists[i][0].lastIndexOf(".")); var title = fullists[i][1][j][1] || fullists[i][1][j][0].substring(fullists[i][1][j][0].lastIndexOf("/")); fullists[i][2][j].innerHTML = header + "<br /><b>" + title + "</b>"; // 用IIFE捕获当前的i和j fullists[i][2][j].addEventListener("click", (function(currentI, currentJ) { return function() { PlayMusic(currentI, currentJ); }; })(i, j), false); fullists[i][3][j] = 0; } }
3. 用bind()预设参数
利用Function.prototype.bind()创建一个绑定了预设参数的新函数,这样点击时会把当前的i和j传给PlayMusic:
var thetable = document.getElementById("mustable"); for(var i=0; i<fullists.length-1; i++) { fullists[i][2] = []; fullists[i][3] = []; for(var j=0; j<fullists[i][1].length; j++) { var row = thetable.insertRow(); fullists[i][2][j] = row.insertCell(); fullists[i][2][j].className = "musentry"; var header = fullists[i][0].substring(0,fullists[i][0].lastIndexOf(".")); var title = fullists[i][1][j][1] || fullists[i][1][j][0].substring(fullists[i][1][j][0].lastIndexOf("/")); fullists[i][2][j].innerHTML = header + "<br /><b>" + title + "</b>"; // bind第一个参数是this指向,这里不需要的话传null fullists[i][2][j].addEventListener("click", PlayMusic.bind(null, i, j), false); fullists[i][3][j] = 0; } }
4. 用data属性存储索引
把i和j存在元素的自定义data属性里,点击时从元素上读取:
var thetable = document.getElementById("mustable"); for(var i=0; i<fullists.length-1; i++) { fullists[i][2] = []; fullists[i][3] = []; for(var j=0; j<fullists[i][1].length; j++) { var row = thetable.insertRow(); fullists[i][2][j] = row.insertCell(); fullists[i][2][j].className = "musentry"; // 存储索引到data属性 fullists[i][2][j].dataset.i = i; fullists[i][2][j].dataset.j = j; var header = fullists[i][0].substring(0,fullists[i][0].lastIndexOf(".")); var title = fullists[i][1][j][1] || fullists[i][1][j][0].substring(fullists[i][1][j][0].lastIndexOf("/")); fullists[i][2][j].innerHTML = header + "<br /><b>" + title + "</b>"; // 点击时读取data属性 fullists[i][2][j].addEventListener("click",function() { var currentI = parseInt(this.dataset.i); var currentJ = parseInt(this.dataset.j); PlayMusic(currentI, currentJ); }, false); fullists[i][3][j] = 0; } }
内容的提问来源于stack exchange,提问作者Edward
相关产品推荐
相关产品推荐

