如何在JavaScript/jQuery的onclick中传递动态对象?
解决JavaScript/jQuery onclick事件传递动态对象的问题
我来帮你梳理下代码里的问题,然后给出几个靠谱的解决方案:
问题分析
你的代码里product变成undefined主要有这几个原因:
- 变量提升与作用域问题:用
var声明的i是函数级作用域,你在循环外就执行了hpc = handleProductsClick.bind(products[i]);,此时i还没初始化,值是undefined,所以products[i]自然也是undefined。 - 全局变量覆盖:你把
hpc设为全局变量,循环里每次赋值都会覆盖之前的,最后所有<li>的onclick都指向最后一次绑定的函数。 - 循环闭包陷阱:就算把
bind放到循环里,var声明的i会在循环结束后变成products.length,导致所有绑定的上下文都是products[products.length](也就是undefined)。
解决方案
方案1:用事件委托(推荐,优雅且无全局污染)
事件委托是最推荐的方式,不用写内联onclick,通过父元素监听子元素的点击事件,还能避免动态生成元素的事件绑定问题:
// 点击事件处理函数 function handleProductsClick(e) { // 从元素的data属性里取出序列化后的product const product = JSON.parse(decodeURIComponent($(e.target).data('product'))); alert(JSON.stringify(product)); } function buildProducts(products) { let bp = ''; for (let i = 0; i < products.length; i++) { // 将product序列化为字符串并存入data属性 const productStr = encodeURIComponent(JSON.stringify(products[i])); bp += `<li data-product="${productStr}">${products[i].value}</li>`; } $("#products").html(bp); // 给父元素ul绑定事件委托 $("#products").on('click', 'li', handleProductsClick); } function getProductsFromApi() { const products = [{ key: 1, value: "Apple" }, { key: 2, value: "Android" } ]; buildProducts(products); } getProductsFromApi();
方案2:动态创建元素并绑定闭包事件
这种方式直接用jQuery创建DOM元素,通过闭包捕获当前循环的product,避免字符串拼接的问题:
// 返回一个绑定了当前product的点击处理函数 function createProductClickHandler(product) { return function(e) { alert(JSON.stringify(product)); } } function buildProducts(products) { const $productList = $("#products").empty(); for (let i = 0; i < products.length; i++) { // 创建li元素并设置文本 const $li = $('<li>').text(products[i].value); // 绑定点击事件,通过闭包传入当前product $li.click(createProductClickHandler(products[i])); // 添加到列表中 $productList.append($li); } } function getProductsFromApi() { const products = [{ key: 1, value: "Apple" }, { key: 2, value: "Android" } ]; buildProducts(products); } getProductsFromApi();
方案3:修复原代码的作用域问题(不推荐,仅作参考)
如果你坚持要用内联onclick,可以用let解决作用域问题,同时避免全局变量覆盖:
function handleProductsClick(product, e) { alert(JSON.stringify(product)); } function buildProducts(products) { let bp = ''; for (let i = 0; i < products.length; i++) { // 用let创建块级作用域,每次循环的i都是独立的 const handler = handleProductsClick.bind(null, products[i]); // 给每个handler分配唯一的全局名称(不推荐,会污染全局) window[`productHandler_${i}`] = handler; bp += `<li onclick="productHandler_${i}(event)">${products[i].value}</li>`; } $("#products").html(bp); } function getProductsFromApi() { const products = [{ key: 1, value: "Apple" }, { key: 2, value: "Android" } ]; buildProducts(products); } getProductsFromApi();
内容的提问来源于stack exchange,提问作者Abraham Gnanasingh
相关产品推荐
相关产品推荐

