首页 > 解决方案 > 如何将唯一的可访问/可传递值烘焙到多个显示按钮中?

问题描述

背景故事:

• 不同的动态项目(按钮)将生成并显示在单个 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);
}

标签: javascript

解决方案


我看到的唯一问题是您将相同的事件侦听器传递给每个新创建的按钮。更重要的是,您正在传递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 的回答是一个特别好的解释。


推荐阅读