如何将唯一的可访问/可传递值烘焙到多个显示的按钮中?



背景故事:

• 不同的动态项目(按钮(将生成并显示在单个div中。

• 每个按钮都是从具有唯一 ID 值的唯一对象创建的。

问题:

如何让每个生成和显示的按钮保留,然后在单击时传递其唯一的"id"?

到目前为止,我的所有努力都得到了"未定义"或仅显示最后生成的 id 值的结果,无论单击哪个按钮。此外,针对 DOM 元素的东西似乎也无济于事,因为我的每个独特项目都不会在它自己的元素中。而只是在单个元素中列出。

就想法/答案而言,我追求的是简单/可读性与速度/效率。我也试图将尽可能多的功能保留在javascript端,并且除了"显示"内容之外,尽可能少地依赖HTML。

以下代码为我按预期工作,没有我的问题:

var allItems = [
{id:1, name:"Space Gem", power:100},
{id:14, name:"Time Gem", power:200},
{id:22, name:"Reality Gem", power:300}
];
var map = {
tile: [
{id:22},
{id:1}
]
}
onTile();
function onTile() {
for ( var i = 0; i < map.tile.length; i++ ) {
var itemId = map.tile[i].id;    
for (var j = 0; j < allItems.length; j++) {
if (itemId === allItems[j].id) {
var itemName = allItems[j].name;
var button = document.createElement("button");
button.innerHTML = itemId + " " + itemName;
document.getElementById("tile_display").appendChild(button);
button.addEventListener ("click", get, false);
}
}   
}
}
function get(itemId) {
alert ("You clicked button with ID: " + itemId);
}

我看到的唯一问题是您将相同的事件侦听器传递给每个新创建的按钮。更重要的是,您传递了get函数,但没有指定参数 - 这意味着当函数响应单击而运行时,itemId将始终undefined。(我现在意识到这不是真的 -itemId将引用对应于刚刚发生的click事件的 Event 对象 - 但在这种情况下这对你没有用。

所以我认为,你需要做的就是改变:

button.addEventListener ("click", get, false);

自:

button.addEventListener ("click", function() {get(itemId);}, false);

编辑:所以这解决了"未定义"的问题。但正如您注意到的,两个按钮都得到"id:1"。这是因为事件侦听器是其封闭作用域的"闭包",这里是onTile函数。这意味着,当您单击该按钮并且事件侦听器运行时,它会查找itemId的值,即使该范围已被销毁,它仍然可以访问该值。但是该作用域中只有一个itemId,并且它具有函数完成执行时具有的任何值(此处为 1( - 每个事件侦听器的值相同。

到目前为止,最简单的修复方法是假设您在支持 ES6 的浏览器中运行(现在就是所有这些浏览器,尽管总是让我惊讶的是有多少仍在使用不支持它的 IE(,只需将var ItemId = ...更改为let ItemId = ...。这样做会ItemId一个新的范围,即循环本身的作用域 - 因此每次通过循环"捕获"一个不同的值 - 完全按照您想要的方式。

如果你确实需要支持 ES6 之前的浏览器,你可以在没有let的情况下执行相同的"技巧",方法是将外部for循环的整个主体包含在一个函数中(这每次都会创建一个新作用域(,然后立即调用它,如下所示:

function onTile() {
for ( var i = 0; i < map.tile.length; i++ ) {
(function() {
var itemId = map.tile[i].id;    
for (var j = 0; j < allItems.length; j++) {
if (itemId === allItems[j].id) {
var itemName = allItems[j].name;
var button = document.createElement("button");
button.innerHTML = itemId + " " + itemName;
document.getElementById("tile_display").appendChild(button);
button.addEventListener ("click", function() 
{get(itemId);}, 
false);
}
}
})();  
}
}
function get(itemId) {
alert ("You clicked button with ID: " + itemId);
}

Javascript闭包,特别是它们如何与这样的循环交互,是一个棘手的话题,已经引起了很多人的注意 - 所以有很多关于它的SO帖子。循环中的JavaScript闭包 - 简单的实际示例就是一个例子,woojoo66的答案是一个特别好的解释。

这里需要做的只是对新创建的按钮使用onClick = function() {}属性,并直接在那里指定 itemId,如下所示:

button.onclick = function() {
get(itemId);
}

您可以在像make_button(itemId) { }这样的小函数中轻松实现这一点(见下文(

make_button(1);
make_button(2);
make_button(3);
make_button(4);
make_button(5);
function make_button(itemId) {
var button = document.createElement("button");
button.onclick = function() {
get(itemId);
}
button.innerHTML = "button " + itemId;
document.getElementById("mydiv").appendChild(button);
}
function get(_itemId) {
alert("You picked button " + _itemId);
}
<div id="mydiv">
</div>

一个更简单的方法是做这样的事情:

var allItems = [{
id: 1,
name: "Space Gem",
power: 100
},
{
id: 14,
name: "Time Gem",
power: 200
},
{
id: 22,
name: "Reality Gem",
power: 300
}
];
var map = {
tile: [{
id: 22
},
{
id: 1
}
]
}
onTile();
function onTile() {
for (var i = 0; i < map.tile.length; i++) {
var itemId = map.tile[i].id;
/* filter out only items in allItems[] that have id === itemId */
var items = allItems.filter(item => item.id === itemId);
/* loop through those few items and run function make_button */
items.forEach(function(item) {
make_button(item.id, item.name);
});
}
}
/* avoid the use of function names such as 'get'  'set' and other commonly  used names as they may conflict with other scripts or native javascript */  
function make_button(itemId, itemName) {
var button = document.createElement("button");
button.innerHTML = itemId + " " + itemName;
button.onclick = function() {
get_clicked_tile(itemId); // changed name from 'get'
};
document.getElementById("tile_display").appendChild(button);
}
function get_clicked_tile(itemId) {
alert("You clicked button with ID: " + itemId);
}
<div id="tile_display"></div>

最新更新