Created by Manuel Lorenz
Last Updated on April 16, 2018

# querySelector vs querySelectorAll

Make sure to read/ watch the previous article/ video where we implement JavaScript to open and close our modal

In the first part of this series, we learned that querySelector is a great method to select DOM elements in JavaScript with CSS selectors. We also saw that this method only returns the first element matching the CSS selector applied - in case two or more elements share the same selector (a CSS class for example), selecting all of these elements won’t work.

Using querySelectorAll instead of querySelector will fix this problem as this method gives us access to all elements matching our CSS selector (the .trip-image class in our case):

var imageSF = document.querySelectorAll('.trip-image')

But how does this method select our elements? Let’s use

console.log(imageSF)

and take a closer look in the browser: Node list in the browser querySelectorAll doesn’t return a single item, it returns both images in a Node List. This Node List adds important information to our elements:

  • An index number (0 for the first element, 1 for the second one)
  • A length, 2 in our example

With this information, JavaScript can now differentiate between these elements, which allows us to access both images (you remember: The modal should open after we click onto each of the images).

Let’s adjust our console.log:

console.log(imageSF[0])

Now we can access the image with the index 0, so the left image on our website, changing [0] to [1] selects the second image.

That’s awesome, because now we only need to ensure, that our openModal function gets called no matter if we click onto the left or the right image.

# Using addEventListener

We learned that we can access our Node List elements with imageSF[0] and imageSF[1], but how can we call our openModal function now?

In the previous article we used .onclick to react to a click onto the image, this time we will use an alternative - an EventListener, which, as the name indicates, can listen to different events (a “click” for example).

The following code will call the openModal function whenever we click onto the image with the index 0 in the Node List:

imageSF[0].addEventListener('click', openModal)

As a sidenote: addEventListener can of course not only be used for click events, in the event reference on the MDN you can find various events that we can listen to.

Now we need to add this code a second time for imageSF[1]. After that, the modal opens, no matter if we click onto the first or onto the second image.

Repeating an almost identical code two times is probably not the best solution though. And assuming that we would have more than two images, copying and pasting the same code again and again would quickly become a nightmare.

# Adding a “for” Loop

Nobody likes nightmares, especially when it comes to repeating certain code blocks. For this purpose, loops can make our lives a lot easier as this is exactly what they are made for: Repeating the same code again and again, but each time with a changing variable. Let’s understand how we can use such a loop and a variable for our goals:

We could use a for loop to make our code leaner, but what information do we need to ensure that the loop works correctly?

addEventListener is still required as we want to react to the “click” event, but why don’t we use a variable like i instead of 0 and 1?

imageSF[i].addEventListener('click', openModal)

This is also the code block for our loop.

With that variable defined and the knowledge that this variable should either be equal to 0 or 1, using the same variable with a value of 0 might be a good starting point for our loop:

for (var i = 0) {
    imageSF[i].addEventListener('click', openModal);
}

That’s the starting point, but when should the loop end? Our Node List includes 2 items, the first with the number 0, the second one with the number 1, so continuing our loop as long as i < 2 probably makes sense.

In the console.log we saw that the length of our NodeList is 2, using i < imageSF.length (so the loop should continue as long as i is smaller than 2) therefore allows us to easily define how long our loop should keep running:

for (var i = 0; i < imageSF.length) {
    imageSF[i].addEventListener('click', openModal);
}

Ok, we also know when our loop ends, but what happens after each loop? Adding i++ as ascendant operator (incrementing i by one integer after each loop) is what we need here:

for (var i = 0; i < imageSF.length; i++) {
  imageSF[i].addEventListener('click', openModal)
}

With that loop we made our code a lot leaner and more flexible: The loop starts by setting i = 0, meaning that imageSF accesses the first image (Node List item 0) and checks if a click event occurs. If that’s the case openModal will be called.

After this first loop, i++ increases i to 1, after that the loop checks if the condition (i < imageSF.length) is still met. That’s the case, so the loop starts once again, now with imageSF[1].

Finally, i increases to 2, the condition is no longer fulfilled as i is now equal to imageSF.length and the loop ends.

As a last step we could also use addEventListener to close the modal:

backdrop.addEventListener('click', closeModal)

That’s of course optional, but now we are able to listen to a click event for both images, call the openModal function in case such an event occurs and close the modal by calling closeModal once we click onto the backdrop - awesome, isn’t it?