如何附加事件侦听器以动态生成单选按钮?

How do I attach eventListeners to dynamically generated radio buttons?

我想为每个单选输入点击附加一个事件侦听器,以便我可以检索该单选输入的值并将其分配为 allQuestions 数组中当前问题对象中的新 属性 的值.

我不确定为什么我在 choice.addEventListener 工作时遇到困难。

我的HTML:

<div id="quiz"></div>
<button id="button">Next</button>

我的JavaScript:

var allQuestions = [{question: "What is the capital of the Czech Republic?", 
                choices: ["Skopje", "Budapest", "Prague", "Bucharest"], 
                correctAnswer: 2},

                {question: "When was the Declaration of Independence signed?",
                choices: ["1492", "1776", "1812", "1791"],
                correctAnswer: 1}];

var quiz = document.getElementById("quiz");
var index = -1;

var next = function() {
    index += 1;
    quiz.innerHTML = "";
    for (var i = 0; i < allQuestions[index]['choices'].length; i++) {
        var choice = document.createElement('input');
        choice.type = 'radio';
        choice.name = 'choices';
        choice.value = i;
        choice.addEventListener('click', function() {
            alert('hi');
        });

        quiz.appendChild(choice);
        quiz.innerHTML += allQuestions[index]['choices'][i] + '<br>';
    }
};

var button = document.getElementById('button');
button.addEventListener('click', next);

您正在尝试同时使用标记和 DOM 元素。这是主要问题:

quiz.appendChild(choice);
quiz.innerHTML += allQuestions[index]['choices'][i] + '<br>';

第一行将带有附加事件处理程序的 DOM 元素附加到 quiz 元素。第二行将 quiz 的内容转换为 HTML 字符串,向该字符串附加一些进一步的文本(标记),然后解析 HTML 和 替换 quiz 的内容和解析后的结果。这会清除 HTML 中未表示的所有内容,包括 dynamically-added 事件处理程序。

解决方案是不做 innerHTML += ...,这几乎总是一个坏主意。在这种特殊情况下,您可以这样做:

quiz.appendChild(choice);
quiz.appendChild(document.createTextNode(allQuestions[index]['choices'][i]));
quiz.appendChild(document.createElement('br'));

...这还有一个优点,即 HTML 中的任何特殊字符(如 <)都按字面意思处理(因为那是 createTextNode 所做的)。


话虽如此,您不需要在每个单选按钮上都使用处理程序。相反,您可以通过在 quiz 元素上使用处理程序来使用 事件委托 ,然后在处理程序中使用 e.target 来了解单击了哪个单选按钮:

quiz.addEventListener("click", function(e) {
    if (e.target.tagName.toUpperCase() === "INPUT") {
        var value = e.target.value; // The value of the clicked radio button
    }
});

这样,您就不必担心动态添加处理程序;只需执行一次,在已知 quiz 元素存在之后,您可以根据需要更新其内容,而不必担心将处理程序附加到其中的单选按钮。

一般来说,要进行事件委托,您通常必须从 e.target 开始循环并转到其 parentNode 以找到您感兴趣的元素(假设您感兴趣在 div 中,但点击是在其中的 span 上);当然,input元素里面不能有任何元素,所以这里就简单了。

您可能想知道为什么要检查标签名称。如果您将单选按钮与 label 元素相关联(这通常是好的做法),可以将 input 放在 label 中,或者使用label 上的 for 属性,当您单击 label 时,您将看到 两次 次点击:一次在标签本身上,另一次在标签相关的 input 上。我们只对实际单选按钮上的那个感兴趣。

下面是上面委托的一个简单示例:

var quiz = document.getElementById("quiz");
quiz.addEventListener("click", function(e) {
    if (e.target.tagName.toUpperCase() === "INPUT") {
        var value = e.target.value; // The value of the clicked radio button
        console.log("The value of that radio button is " + value);
    }
});
for (var n = 0; n < 5; ++n) {
    var div = document.createElement("div");
    div.innerHTML = '<label><input type="radio" value="' + n + '" name="answer"> Radio ' + n + '</label>';
    quiz.appendChild(div);
}
<div id="quiz"></div>