Reputation: 1604
I want to attach an event listener to each radio input click, so that I can retrieve that radio input's value and assign it as the value of a new property in the current question object in the allQuestions array.
I'm not sure why I'm having difficulty making choice.addEventListener
work.
My HTML:
<div id="quiz"></div>
<button id="button">Next</button>
My 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);
Upvotes: 2
Views: 1171
Reputation: 1074148
You're trying to use markup and DOM elements at the same time. Here's the main problem:
quiz.appendChild(choice);
quiz.innerHTML += allQuestions[index]['choices'][i] + '<br>';
The first line appends a DOM element with an attached event handler to the quiz
element. The second line converts the contents of quiz
to an HTML string, appends some further text (markup) to that string, and then parses that HTML and replaces the content of quiz
with the parsed result. That wipes out anything not represented in the HTML, including the dynamically-added event handler.
The solution is not to do innerHTML += ...
, which is almost always a bad idea. In this particular case, you can do this:
quiz.appendChild(choice);
quiz.appendChild(document.createTextNode(allQuestions[index]['choices'][i]));
quiz.appendChild(document.createElement('br'));
...which also has the advantage that any characters that are special in HTML (like <
) are treated literally (because that's what createTextNode
does).
Now, having said that, you don't need handlers on every radio button. Instead, you can use event delegation by using a handler on your quiz
element, and then using e.target
within the handler to know which radio button was clicked:
quiz.addEventListener("click", function(e) {
if (e.target.tagName.toUpperCase() === "INPUT") {
var value = e.target.value; // The value of the clicked radio button
}
});
That way, you don't have to worry about adding handlers dynamically; just do it once, after the quiz
element is known to exist, and you can update its content all you want without having to worry about attaching handlers to the radio buttons within.
To do event delegation in general, you usually have to loop starting from e.target
and going to its parentNode
in order to find the element you're interested in (say you're interested in a div
, but the click was on a span
inside it); but of course, input
elements can't have any elements inside them, so it's simple here.
You're probably wondering why the check on the tag name. If you associate your radio buttons with label
elements (as is usually good practice), either by putting the input
inside the label
or using the for
attribute on the label
, you'll see two clicks when you click the label
: One on the label itself, and a second one on the input
that the label relates to. We're only interested in the one on the actual radio button.
Here's a simple example of the delegation above:
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>
Upvotes: 4