Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript JavaScript and the DOM (Retiring) Responding to User Interaction Listening for Events with addEventListener()

Saqib Ishfaq
Saqib Ishfaq
13,912 Points

why cant we use "querySelectorAll" instead ,? and may be avoid using for loop! i tried its not working......why?

const listItems = document.querySelectorAll('li');

listItems.addEventListener ('mouseover', () => {
listItems.textContent = listItems.textContent.toUpperCase();
});

gives me this error Uncaught TypeError: listItems.addEventListener is not a function at app.js:11

Saqib Ishfaq
Saqib Ishfaq
13,912 Points

Jennifer Nordell This works fine, so today i exactly know what you meant couple of months ago :)

const listItems = document.querySelectorAll('li');

for(let i = 0; i< listItems.length; i +=1){
  listItems[i].addEventListener('mouseover' , () => {
    listItems[i].textContent = listItems[i].textContent.toUpperCase();
  });
  listItems[i].addEventListener('mouseout', () => {
    listItems[i].textContent = listItems[i].textContent.toLowerCase();
  });
}

3 Answers

Jennifer Nordell
seal-mask
STAFF
.a{fill-rule:evenodd;}techdegree
Jennifer Nordell
Treehouse Teacher

Hi there! When I was first learning, I had this thought too. But think about what you've learned about arrays thus far. That querySelectorAll is not returning an element of the DOM, it's returning an array of elements from the DOM. You've learned already quite a few methods that exist on the Array object in JavaScript such as push, pop, shift, and unshift. The addEventListener is not a valid method on the Array object. Here is the MDN documentation.

The addEventListener can only be added to a compatible object such as the window or an element in the DOM. So we need to loop over the Array and attach the event listener to each element in the array. Here is the MDN documentation for addEventListener.

Hope this clarifies things! :sparkles:

edited for clarification At the time I wrote this answer, I believed that a node collection was an array of DOM nodes. It is, however, not technically an array, but functions much like one. We access its elements in much the same way as an array and the NodeList object can easily be converted to an array. However, addEventListener is still not a valid method on the NodeList object nor the HTMLCollection as evidenced by this MDN documentation and here.

Saqib Ishfaq
Saqib Ishfaq
13,912 Points

Means there's nothing wrong using "querySelectorAll" on 'li' as long as u use [0] with it for the 1st item in that array and use loop , to go over each item in that array ,and attach "addEventListener" method to it ?? Let me giv it a go ! M out rite now but I ll once m home !

Jennifer Nordell
seal-mask
.a{fill-rule:evenodd;}techdegree
Jennifer Nordell
Treehouse Teacher

What you just described is exactly what he does in the video. He loops over the array and attaches the event listener to every element in the array using the subscript. The first item in the array is at index 0 as you say, and the for loop begins with i = 0. So when he says listItems[i], at the first iteration, i will be equal to 0.

Saqib Ishfaq
Saqib Ishfaq
13,912 Points

Only thing i m confused abt is the selection stage , why prefer "getElementsByTagName " over "querySelectorAll" ??? Which he did in the video ! If we can still loop through if we use querySelectorAll then why getElem......

Jennifer Nordell
seal-mask
.a{fill-rule:evenodd;}techdegree
Jennifer Nordell
Treehouse Teacher

It might not be obvious in the video and this is a bit more of an advanced topic, but a querySelectorAll() has some parsing going on under the hood which makes it slightly less efficient than getElementsByTagName(). So if you're searching just for all paragraphs, all divs, all spans etc, you'd probably want to use the getElementsByTagName. However, if you need a bit of a trickier DOM traversal where you want all list items but only with the class name "my-item", then a querySelectorAll('.my-item li') would probably be your optimal choice so that you don't have to set up a bunch of unnecessary variables to hold your DOM traversal.

Saqib Ishfaq
Saqib Ishfaq
13,912 Points

ok i can remember that for now and may be refer to it when we proceed ahead in the course... so just to clarify m i correct to say , or rather is it a gud practice for me to use "querySelector" when we selecting an element with classes and ids and use "getElement......." when we selecting, just the tags like with no class and id etc .... ????

Jennifer Nordell
seal-mask
.a{fill-rule:evenodd;}techdegree
Jennifer Nordell
Treehouse Teacher

Not exactly. What I mean is that if you don't have more than one selector, it's better to use the getElementsBy or getElementBy, but if you have more than one selector it's better to use the querySelector. I feel like this will become more clear for you as you advance. :smiley:

Aakash Srivastav
seal-mask
.a{fill-rule:evenodd;}techdegree
Aakash Srivastav
Full Stack JavaScript Techdegree Student 11,638 Points

You said addEventListener id not a valid method on the Array object. But isn't getElementsByTagName too returning an array of elements with a given tag name? The only difference is getElementsByTagName return live HTMLCollection of elements while querySelectorAll returns static nodeList .
But what I want to say is Both interfaces(nodeList and HTMLCollection) are collections of DOM nodes then what the meaning of ----- addEventListener is not a valid method on the Array object?

Jennifer Nordell
seal-mask
.a{fill-rule:evenodd;}techdegree
Jennifer Nordell
Treehouse Teacher

Aakash Srivastav Can we agree that a node list functions in much the same way as an array? It is a collection of nodes from the DOM, even if it contains only one element. You cannot add an event listener to a collection, rather you must add an event listener to a node or element in that collection. Again, check the MDN documentation for addEventListener linked above.

Also, node lists can also be easily converted to an array (probably just for this reason). The addEventListener is also not a valid method on a node list as documented here. However, I will update the information above with new documentation. This is also true for HTMLCollection which also does not have the addEventListener as a valid method as documented here.

Saqib Ishfaq
Saqib Ishfaq
13,912 Points

i just wish it was little more clearer than it is in the videos....thanks for the help ,appreciate it!

Arun Vignesh
Arun Vignesh
4,805 Points

Thanks for the post and for the detailed explanation.

I too faced a similar scenario and the code was not working. After reading the comments, i changed my code and it works fine now. Sharing my code below that was updated using document.querySelectorAll() and For Of Loop:

for(let item of listItems){
  item.addEventListener('mouseover', () => {
    item.textContent = item.textContent.toUpperCase();                     
  });

  item.addEventListener('mouseout', () => {
    item.textContent = item.textContent.toLowerCase();                     
  });
}