1

I just started learning Javascript today and decided to start with practical examples, for example a toggle function.


This is what I've been ended with so far:

document.getElementsByClassName("toggle").addEventListener("click", function() {
  if(this.classList.contains("toggled")) {
    this.classList.remove("toggled")
  }
  else {
    this.classList.add("toggled")
  }
})

However it returns the error: document.getElementsByClassName(...).addEventListener is not a function. I figure that I havn't declared the function correctly, but can't figure why. Here's a Codepen.

It's probably less of a problem as what I see it.

Simplicius
  • 2,009
  • 1
  • 5
  • 19
  • `getElementsByClassName` will give you an array of dom elements. So either you loop and bind the listener or take the certain index and it will work. – sibabrat swain Sep 16 '20 at 11:27

4 Answers4

1

The problem with that is that, document.getElementsByClassName() returns array of DOM elements. To bind event to elements, you would have to iterate though each element, and bind event to it. Do it like this :

Convert the HTML collection before iterating :

[...document.getElementsByClassName("toggle")].forEach(function(item){
  item.addEventListener("click", function() {
    if(this.classList.contains("toggled")) {
      this.classList.remove("toggled")
    }
    else {
      this.classList.add("toggled")
    }
  })
});

OR


Using querySelectorAll() :

document.querySelectorAll(".toggle").forEach(function(item){
  item.addEventListener("click", function() {
    if(this.classList.contains("toggled")) {
      this.classList.remove("toggled")
    }
    else {
      this.classList.add("toggled")
    }
  })
});

For detecting click on any element other than with class toggle :

document.querySelector('body').addEventListener('click', function(event){
  if(!event.target.classList.contains('toggle'){
    //code to do
  }
});
Pranav Rustagi
  • 2,604
  • 1
  • 5
  • 18
1

The method document.getElementsByClassName (elements) returns an array of elements with this class. So when you want to access elements of the array you has to to something like:

let firstElement = document.getElementsByClassName("toggle")[0]

If you want to access exactly one element, use document.getElementById or document.querySelector('.toggle')

michaelT
  • 1,533
  • 12
  • 41
1

Simply add the index of element. In your case 0

document.getElementsByClassName("toggle")[0].addEventListener("click", function() {
  if(this.classList.contains("toggled")) {
    this.classList.remove("toggled")
  }
  else {
    this.classList.add("toggled")
  }
})
  • Since you have only one element with className "toggle", its 0 (in your case). But one thing to remember is that except for Id selector, for all selectors (clas, tag etc.,) we must use index. – Saravan Somanchi Sep 16 '20 at 11:28
  • Yes, if there was a second, which I wanted to target id use `[1]`, since JS is `0` based, that's what I meant. – Simplicius Sep 16 '20 at 11:30
1

Please prefer querySelectorAll over getElementsByClassName. In both cases you will give you an array of dom elements. So either you loop and bind the listener or take the certain index and it will work

const toggleButtons = document.querySelectorAll(".toggle")
if(toggleButton.length > 0) {
    toggleButtons[0].addEventListener('click', function(event) {
       if (this.classList.contains('toggled')) {
            this.classList.remove('toggled');
        } else {
            this.classList.add('toggled');
        }
    })
}
sibabrat swain
  • 1,277
  • 8
  • 20
  • So the `if` statement is meant to check wether `.toggle` exists and only execute if this condition is met, right? But why prefer `querySelectorAll`? – Simplicius Sep 16 '20 at 11:34
  • yes, it is to check else it throws an exception. Because querySelectorAll() returns a list that is static from the moment it is called, its list of items cannot be updated thereafter even if changes are made to the DOM dynamically – sibabrat swain Sep 16 '20 at 11:36
  • Nice to know. Sorry if I ask too much, but why would I prefer this feature? – Simplicius Sep 16 '20 at 11:38
  • in your case, there is no measure difference considering `querySelectorAll `. But I would like to suggest to use `querySelectorAll` and `querySelector`. Here you don't need to remember all the other selectors and most of the dom operations can be done using these. – sibabrat swain Sep 16 '20 at 11:43
  • 1
    Got it. Thanks for you explanation! – Simplicius Sep 16 '20 at 11:44
  • You are welcome. I could not explain all the differences here in the comments, but there are many advantages using these. Consider visiting this link for more info. https://stackoverflow.com/questions/14377590/queryselector-and-queryselectorall-vs-getelementsbyclassname-and-getelementbyid – sibabrat swain Sep 16 '20 at 11:49