JavaScript循环中为按钮添加点击事件

3
以下代码从xml文件获取信息。我成功地使用按钮呈现了每个行星的id和名称。
我想在按钮上添加一个onclick事件。 问题是:它确实添加了onclick事件,但仅在循环中创建的最后一个按钮上。
我做错了什么?为什么它不为每个按钮创建一个onclick事件,而只为循环中的最后一个按钮创建一个?
function updatePlaneten() {
    var valDiv, planets, valButton, textNode;
    // Get xml files
    planets = this.responseXML.getElementsByTagName("planeet");
    // loop through the <planet> tags 
    for (var i = 0; i < planets.length; i++) {
        valDiv = ''; // clear valDiv each time loop starts

        // Get the id and the name from the xml info in current <planet> tag
        valDiv += planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue + "<br>";
        valDiv += planets[i].getElementsByTagName("name")[0].childNodes[0].nodeValue + "<br>";
        document.getElementById("planetenID").innerHTML += valDiv + "<br>";

        // Create button with a value and pass in this object for later reference use (valButton.object=this)
        valButton = document.createElement("input");
        //  valButton.setAttribute("planeetID", planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue);
        valButton.setAttribute("value", 'Meer info');
        valButton.setAttribute("type", 'button');
        valButton.id = (i + 1);
        valButton.object = this;    
        //
        // Here is the problem i cant get fixed
        //    
        //valButton.onclick = function(){ showinfo(); }
        valButton.addEventListener('click', showinfo);
        // Place the button on screen
        document.getElementById("planetenID").appendChild(valButton);
    }
}

// simple function to check if it works
function showinfo() {
    console.log(this.object);
    console.log(this.id);
}

使用闭包实现:https://dev59.com/6XVD5IYBdhLWcg3wBm5h - AlwaysNull
@Andreas,这不是重复的问题,因为OP每次设置innerHTML时都会销毁按钮,从而有效地销毁了先前设置的事件侦听器。 - Patrick Evans
@PatrickEvans 您说得完全正确。我应该更仔细地阅读代码... :( - Andreas
2个回答

2
问题在于这一行:
document.getElementById("planetenID").innerHTML += valDiv + "<br>";

当你设置innerHTML时,当前内容被销毁并替换为新的html,这意味着所有旧按钮现在都被销毁并创建了新的按钮。之前附加的事件监听器不会附加到新按钮上。
相反,只需创建一个div / span或任何最好的容器,将行星文本或任何其他内容添加到其中,然后使用appendChild。
valDiv = document.createElement("div");
var id = planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue;
var name = planets[i].getElementsByTagName("name")[0].childNodes[0].nodeValue;
valDiv.innerHTML = id+"<br>"+name+"<br>";
document.getElementById("planetenID").appendChild(valDiv);

您还可以使用insertAdjacentHTML
var id = planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue;
var name = planets[i].getElementsByTagName("name")[0].childNodes[0].nodeValue;
valDiv = id+"<br>"+name+"<br>";
document.getElementById("planetenID").insertAdjacentHTML("beforeend",valDiv);

0
function updatePlaneten() {
    var valDiv, planets, valButton, textNode;
    // Get xml files
    planets = this.responseXML.getElementsByTagName("planeet");
    // loop through the <planet> tags 
    for (var i = 0; i < planets.length; i++) {

        (function(num){

            valDiv = document.createElement("div");

            // Get the id and the name from the xml info in current <planet> tag
            var id = planets[num].getElementsByTagName("id")[0].childNodes[0].nodeValue + "<br>";
            var name = planets[num].getElementsByTagName("name")[0].childNodes[0].nodeValue + "<br>";
            valDiv.innerHTML = id+"<br>"+name+"<br>";
           document.getElementById("planetenID").appendChild(valDiv);

            // Create button with a value and pass in this object for later reference use (valButton.object=this)
            valButton = document.createElement("input");
            //  valButton.setAttribute("planeetID", planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue);
            valButton.setAttribute("value", 'Meer info');
            valButton.setAttribute("type", 'button');
            valButton.id = (num + 1);
            valButton.object = this;    
            // FIX: PASS showinfo TO AN ANONYMOUS FUNCTION CONTAINING THE OBJECT
            valButton.addEventListener('click', function(){
             showinfo(valButton);   
            });
            // Place the button on screen
            document.getElementById("planetenID").appendChild(valButton);

        }(i));


    }
}

// simple function to check if it works
function showinfo(valButton) {
    console.log(valButton.object);
    console.log(valButton.id);
}

IIFE(立即调用函数表达式)通常是解决这个问题的方法,但 showinfo 已经有了作用域,并且他没有在监听器内部使用 i。你能解释一下为什么在这里使用 IIFE 是正确的吗? - A1rPun
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Patrick Evans
非常感谢,这解决了问题。 - Walter Pothof
还有一个问题,我在哪里可以找到更多关于 (function(num){ ... }(i)); 这个方法或函数的信息?虽然它解决了我的问题,但我还不理解它背后的运作原理。 - Walter Pothof
这被称为IIFE。我认为,更能澄清事情的是这篇文章 - Kostas Minaidis
@WalterPothof,IIFE并没有解决你的问题。在这里,除非你最终在回调函数中使用循环变量,否则不需要使用IIFE。问题很简单,就是通过追加innerHTML来破坏和重新创建HTML。 - Patrick Evans

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接