JS 在函数定义中使用 for 循环变量,在迭代过程中函数不会改变
JS Use for loop variable in function definition, without the function changing during iterations
我正在编写一个游戏,其中用户必须 select 从后端发送的其中一张卡片,但在收到之前我不知道是什么卡片或多少张卡片。我必须为每个按钮制作一个按钮,并将选择的按钮发送到后端。因为我不知道会有多少张卡片,所以我在for循环中定义了每个按钮的动作。
由于我使用循环变量访问必须发送到后端的数字代码,因此在每次循环迭代后,函数发送的数字代码都会发生变化。
我是这样解决问题的:
(cards是一个包含卡片的数组,对于其中的任何一张卡片,card['code']是它的数字代码。sendJSON中的11表示发送的信息是选择的卡片)
for (var i = 0; i < cards.length; i++) {
container.innerHTML += `<button><img src="Cards/${cards[i]['code'].toString()}.png" width="346" height="529" id="${cards[i]['code'].toString()}"></button>`;
// Here is the problem:
eval('document.getElementById(' + cards[i]['code'].toString() + ').onclick = async function f() {await sendJSON(11, ' + cards[i]['code'] + ')}')
}
问题是 eval() 因为它不仅不安全,而且性能也很差,所以我想知道...
有更好的方法吗?
您可以将代码存储为 data-*
属性 并对每个元素使用相同的事件处理程序(或使用注释中提到的事件委托)。我还建议不要将 innerHTML
用于这种简单的标记,尤其是不要在循环内使用(请参阅 Barmar 的回答以了解为什么它无论如何都不起作用):
function handler(event) {
sendJSON(11, event.dataset.code);
}
for (var i = 0; i < cards.length; i++) {
const img = document.createElement('img');
img.src = `Cards/${cards[i]['code']}.png`;
img.dataset.code = 'code';
img.width = 346;
img.height = 529;
img.onclick = handler;
const button = document.createElement('button');
button.appendChild(img);
container.appendChild(button);
}
因为你把代码放在 id
属性中,事件监听器可以简单地使用 this.id
来获取它,你不需要将它复制到函数中。
您还需要使用 insertAdjacentHTML()
而不是连接到 innerHTML
。连接时,容器中的所有 HTML 都将被重新解析,之前图像上的所有事件侦听器都将丢失。 insertAdjacentHTML()
只解析新的 HTML 并保留所有旧元素不变。
for (var i = 0; i < cards.length; i++) {
container.insertAdjecentHTML('beforeend', `<button><img src="Cards/${cards[i]['code']}.png" width="346" height="529" id="${cards[i]['code']}"></button>`);
document.getElementById(cards[i]['code']).addEventListener("click", async function f() {
await sendJSON(11, this.id)
});
}
也不需要所有 toString()
调用。代入模板文字会自动将其转换为字符串。
我正在编写一个游戏,其中用户必须 select 从后端发送的其中一张卡片,但在收到之前我不知道是什么卡片或多少张卡片。我必须为每个按钮制作一个按钮,并将选择的按钮发送到后端。因为我不知道会有多少张卡片,所以我在for循环中定义了每个按钮的动作。
由于我使用循环变量访问必须发送到后端的数字代码,因此在每次循环迭代后,函数发送的数字代码都会发生变化。
我是这样解决问题的:
(cards是一个包含卡片的数组,对于其中的任何一张卡片,card['code']是它的数字代码。sendJSON中的11表示发送的信息是选择的卡片)
for (var i = 0; i < cards.length; i++) {
container.innerHTML += `<button><img src="Cards/${cards[i]['code'].toString()}.png" width="346" height="529" id="${cards[i]['code'].toString()}"></button>`;
// Here is the problem:
eval('document.getElementById(' + cards[i]['code'].toString() + ').onclick = async function f() {await sendJSON(11, ' + cards[i]['code'] + ')}')
}
问题是 eval() 因为它不仅不安全,而且性能也很差,所以我想知道...
有更好的方法吗?
您可以将代码存储为 data-*
属性 并对每个元素使用相同的事件处理程序(或使用注释中提到的事件委托)。我还建议不要将 innerHTML
用于这种简单的标记,尤其是不要在循环内使用(请参阅 Barmar 的回答以了解为什么它无论如何都不起作用):
function handler(event) {
sendJSON(11, event.dataset.code);
}
for (var i = 0; i < cards.length; i++) {
const img = document.createElement('img');
img.src = `Cards/${cards[i]['code']}.png`;
img.dataset.code = 'code';
img.width = 346;
img.height = 529;
img.onclick = handler;
const button = document.createElement('button');
button.appendChild(img);
container.appendChild(button);
}
因为你把代码放在 id
属性中,事件监听器可以简单地使用 this.id
来获取它,你不需要将它复制到函数中。
您还需要使用 insertAdjacentHTML()
而不是连接到 innerHTML
。连接时,容器中的所有 HTML 都将被重新解析,之前图像上的所有事件侦听器都将丢失。 insertAdjacentHTML()
只解析新的 HTML 并保留所有旧元素不变。
for (var i = 0; i < cards.length; i++) {
container.insertAdjecentHTML('beforeend', `<button><img src="Cards/${cards[i]['code']}.png" width="346" height="529" id="${cards[i]['code']}"></button>`);
document.getElementById(cards[i]['code']).addEventListener("click", async function f() {
await sendJSON(11, this.id)
});
}
也不需要所有 toString()
调用。代入模板文字会自动将其转换为字符串。