Listening For Multiple Events & Event Data

Open the project folder

In our starter files, open the index.html page from this lessons folder:

04.Events-And-The-DOM > 10.Listening-for-multiple-events-and-event-data

Starter code

The previous examples added an event listener to a single element. But there may be a time when we want to add the same one to multiple elements.

As an example, if you open the starter project for this lesson, at the bottom of the page we have some recipes. And you will notice a star for the user to click on and add to favourites.

Nothing works just yet, this is just a <span> element which we can see in the index page:

<div class="recipes_container">
  <article>
    <span class="add">&#9734;</span>
    <img src="burger.jpg" alt="" />
    <h4 >Ultimate Vegan</h4>

We can add an event listener to all these stars to add them to a saved list of favourites.

We could also have many more recipes listed and may want to add the same event listener to each one of them. Selecting each recipe and adding the event listener manually would be a lot of work.

Attaching an event listener to all elements

So instead, we can use a loop to help with this. Over to the events.js, first we need to select all these <span> elements:

// events.js
const addButtons = document.querySelectorAll(".add");

And remember from earlier, using querySelectorAll returns a HTMLCollection, which we can loop over using forEach:

const addButtons = document.querySelectorAll(".add");
addButtons.forEach(function(button) {
  // add event listener for each element (function not yet created)
  button.addEventListener('click', addToFavourites)
})

Then we need to create this addToFavourites function:

const addButtons = document.querySelectorAll(".add");

function addToFavourites() {
  alert(`added to favorites`)
}

addButtons.forEach(function(button) {
  button.addEventListener('click', addToFavourites)
})

Each star will now trigger the alert when clicked. This is how we can add an event listener to multiple elements.

Accessing event information

We may also want to know which one of these recipes was clicked. If we are just adding some CSS, or something generic, it does not matter which one. We may want to redirect to a new page with the full recipe. Or, in our case, we maybe want the recipe title to add to a list of favourites. And we can do this, along with many other things by accessing the events data.

In the function, we access it as a parameter:

function addToFavourites(e) {
  alert(`added to favorites`)
}

This name is up to us and it is common to see it called event, or e for short, and it contains the event object. This event object contains a huge amount of data which we can access. Try this out with a console log:

function addToFavourites(e) {
  console.log(e);
  alert(`added to favorites`)
}

Into the console, you will see lots of information logged about the event:

PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0, …}

It is a pointer event since we clicked using the mouse. Since this is an object, we can open it up and find out even more. We see things such as the mouse position when the click occurred, and a useful property is the target:

target: span.add

This is the element which fired the event, it is our <span> with the class of add.

Inside this target section we have lots of information we need about the element, such as the classList which we looked at earlier:

classList: DOMTokenList ["add", value: "add"]

Or the innerText:

innerText: "☆"

We could access this in our code with e.target.innerText. Over in the recipes article:

<article>
  <span class="add">&#9734;</span>
  <img src="burger.jpg" alt="" />
  <h4>Ultimate Vegan</h4>

We now have the information for the <span> element. But how do we get the recipe title from the h4 element?

Back to the console log, we have available the next element information, which is the image:

nextElementSibling: img
nextSibling: text

This would work if our title was immediately after the <span>, but ours is two lines below.

Selecting the parent

For our case we need to first access the parent element. This is available inside the target property:

parentNode: article

This correctly shows the parent as the article. Expand this to see the children property:

children: HTMLCollection(4)
0: span.add
1: img
2: h4
3: div.recipe_info
length: 4

This lists all the elements inside the article as a HTMLCollection. The h4 is at index position two. Over to our function we can now use this information:

function addToFavourites(e) {
  console.log(e.target.parentNode.children[2]);
  alert(`added to favorites`)
}

Click on a star and this will show the title element in the console. Then select the innerText content:

console.log(e.target.parentNode.children[2].innerText);

This is a long way to get the recipe name, but sometimes to get the information we need we must keep digging into the object. This is how you could begin to construct a favourites list, using the names to then push to an array or a database.

To finish, move the recipe title to the alert and remove the console log:

function addToFavourites(e) {
  alert(`${e.target.parentNode.children[2].innerText}`)
}