解决jQuery中点击动态生成竞赛卡片时模态框始终加载最后一个URL的问题
解决点击卡片时模态框总是加载最后一个URL的问题
嘿,这个问题我太熟了——典型的闭包作用域陷阱!问题出在你循环里的变量共享上,所有点击事件监听器都引用了同一个prizeURL变量,导致循环结束后它只会保留最后一次迭代的值。
问题根源
你在循环外部的共享函数作用域里定义了prizeURL,每次迭代只是更新它的值。当点击事件触发时,循环已经跑完了,prizeURL早就变成最后一个卡片的URL了,所以不管点击哪个卡片,都会调用这个最后值。
修复方案(三种简单方法)
方法1:用let声明循环内的变量(最推荐)
把prizeURL的声明移到循环内部,用let替代原来的赋值,这样每次循环都会创建一个独立的变量绑定:
$.each(data, function (key, value) { var prize = ''; var prizeState = value.prizeState; let prizeURL = value.entryForm; // 这里改成let,形成块级作用域 // ... 其他代码不变 ... card.addEventListener('click', function(){ modalPop(prizeURL); console.log(prizeURL, ' from onclick'); }); // ... 其他代码不变 ... });
方法2:直接在事件处理函数中使用value.entryForm
既然value在当前迭代的回调函数作用域里,直接引用它的entryForm属性就行,不用额外变量:
card.addEventListener('click', function(){ modalPop(value.entryForm); // 直接用value.entryForm,绕过共享变量 console.log(value.entryForm, ' from onclick'); });
方法3:用IIFE(立即执行函数表达式)创建独立作用域
这是ES6之前常用的方法,手动为每个迭代创建作用域:
card.addEventListener('click', (function(url) { return function() { modalPop(url); console.log(url, ' from onclick'); }; })(value.entryForm));
额外优化:修复内联onclick的潜在问题
你代码里的<div class="entry-form" onclick="modalPop(' + value.entryForm + ')">存在风险——如果URL包含单引号或特殊字符,会直接导致语法错误。建议也改成用事件监听器绑定:
// 替换原来的prize += '<div class="entry-form"...>'部分 const entryFormDiv = document.createElement('div'); entryFormDiv.classList.add('entry-form'); entryFormDiv.innerHTML = '<span class="entry-url">' + value.entryForm + '</span>'; entryFormDiv.addEventListener('click', function() { modalPop(value.entryForm); }); // 然后把entryFormDiv添加到card里,而不是用字符串拼接innerHTML
修改后的完整代码示例
// FETCHING DATA FROM JSON FILE $.getJSON("https://cdn.shopify.com/s/files/1/2660/5202/files/data.json?v=1624391152", function (data) { var modal = ''; var cardwrapper = document.getElementById('cardWrapper'); // ITERATING THROUGH OBJECTS $.each(data, function (key, value) { var prize = ''; var prizeState = value.prizeState; let prizeURL = value.entryForm; // 用let声明,确保每个迭代独立 // 创建卡片元素 var card = document.createElement('div'); card.setAttribute('data-modal', value.prizeDay); card.classList.add('card'); // 构建卡片内容 prize += '<div class="entry-form"><span class="entry-url">' + value.entryForm + '</span></div>' + ' <div class=" ' + value.prizeState + '">' + '<div class="prizeDay">Day ' + value.prizeDay + '</div>' + '<div class="prizePhoto"> <img src="' + value.prizePhoto + '" /></div>' + '<div class="prizeTitle"> ' + value.prizeTitle + '</div>' + '<div class="prizeWinner">' + value.prizeWinner + ' ' + value.prizeCity + '</div>' + '<span class="button btn btn-default prizeEnterButton">Enter Contest</span>' prize += '</div>'; card.innerHTML = prize; // 绑定卡片点击事件 card.addEventListener('click', function(){ modalPop(prizeURL); console.log(prizeURL, ' from onclick'); }); // 单独绑定entry-form的点击事件(避免内联onclick的问题) card.querySelector('.entry-form').addEventListener('click', function() { modalPop(value.entryForm); }); cardwrapper.appendChild(card); console.log(prizeURL); }); }); // Get the modal var modal = document.getElementById("myModal"); // Get the button that opens the modal var btn = document.getElementById("myBtn"); function modalPop(prizeURL) { console.log(prizeURL); var popupContent = '<h4>' + prizeURL + '</h4>' + '<span id="modalClose" class="close">×</span>' + '<span id="widget-code">' + '<iframe src="' + prizeURL + '" frameBorder="0" allowfullscreen></iframe>' + '</span>' $('#myModal #modalReplace').empty().html(popupContent); $('#myModal').fadeIn(200).addClass('modal-active'); } // When the user clicks anywhere outside of the modal, close it window.onclick = function(event) { if (event.target == modal) { $('#myModal').fadeOut(200); } }
为什么这些方法有效?
let关键字会在每次循环迭代中创建一个新的变量绑定,每个事件监听器都会捕获自己迭代的那个变量实例。- 直接引用
value.entryForm时,value是当前迭代回调函数的参数,属于独立的作用域,不会被后续迭代覆盖。 - IIFE通过立即执行函数,把当前迭代的URL作为参数传入,创建了独立的作用域保存这个值。
内容的提问来源于stack exchange,提问作者BobDiggity
相关产品推荐
相关产品推荐

