Connor
Connor

Reputation: 29

How come the event listener I am adding only works for elements I got by Id instead of by class?

I am trying to make a very simple java script quiz to try and learn the language.

var result = document.getElementById("result");

var rightAnswer = document.getElementById("correct").addEventListener("click",correctAnswer);

function correctAnswer(){
 result.innerText="Correct";
};

var wrongAnswer = document.getElementsByClassName("wrong").addEventListener("click",wrongAnswer);

function wrongAnswer(){
 result.innerText="Wrong";
};

When I run it and test the correct answer works fine but the wrong answers are unresponsive. When I look in the console it says that "document.getElementsByClassName(...).addEventListener is not a function" which confuses me seeing how I formatted it exactly like the other line with the exception of the "getElementsByClassName". Can I not add the same event listener to multiple buttons of the same class? HTML

<!DOCTYPE html>
<html>

<head>
<title>Quiz</title>

</head>

<body>
<p>Who was the first president of the United States?</p>

<button class='button wrong' type='button'>Thomas Jefferson</button>

<button class='button' id='correct' type='button'>George Washington</button>

<button class='button wrong' type='button'>James Madison</button>

<button class='button wrong' type='button'>John F Kennedy</button>

<p id='result'></p>

<script src='quiz.js'></script>

</body>

</html>

Upvotes: 0

Views: 76

Answers (2)

Mitya
Mitya

Reputation: 34556

The error in your error console is because you can't run addEventListener() chained to getElementsByTagName().

getElementsByTagName() returns a nodeset, whereas addEventListener() is a method of the HTML node object, not nodeset object.

In other words, it runs on a single node. This means we either need to iterate over the nodeset and bind the event to each node found, or use event delegation.

Iteration approach

var wrong = document.querySelectorAll(".wrong");
wrong.forEach(function(el) {
    el.addEventListener("click",wrongAnswer, false);
});

Delegation approach

With event delegation, you bind not to each and every node but to a common parent or ancestor. You then interrogate which node triggered the event.

We can rely on this because events "bubble"; they fire not only for the element to which they're bound, but also that element's child/descendant nodes, from the innermost outwards.

In other words, the trigger element (evt.target) and the context element (this) may not be one and the same.

Let's imagine all your .wrong elements live in a common container, with ID #wrong.

We could then do:

document.querySelector('#wrong').addEventListener('click', function(evt) {
    var trigger_el = evt.target;
    if (!evt.target.matches('.wrong') return; //do nothing - was some other element
    wrongAnswer.call(trigger_el); //call wrongAnswer in the context of the trigger element
}, false);

Upvotes: 1

user8209791
user8209791

Reputation:

You need to loop through the result of document.getElementsByClassName("wrong") in order to addEventListeners because getElementsByClassName will always return multiple elements. You can do so simply:

var wrongAnswer = document.getElementsByClassName("wrong");
for(n = 0; n < wrongAnswer.length; n++){
  wrongAnswer[n].addEventListener("click",wrongAnswer);
}

Upvotes: 1

Related Questions