const - JS | lectureDOM & Events Fundamentals

Project II - Working with Classes

DOM & Events Fundamentals

Let's now actually learn how to manipulate classes with JavaScript. The first thing we have to do is react to a click on each of our buttons because nothing happens right now.

Let's start by attaching an event handler to each of these three buttons. We finished the last lecture by learning how we can do something for all of these three buttons at the same time. For that, we used a for loop, which loops over the NodeList, which is basically an array holding all of the buttons.

script.js
// Previous Lecture
for (let index = 0; index < openModalButtons.length; index++)
  console.log(openModalButtons[index].textContent);

With openModalButtons at position number index (openModalButtons[index]), being the current button element in each iteration. From there we simply logged the textContent property just like on any other element.

Let's now take it one step further, and instead of just reading the textContent property, we will call the addEventlistener method, just like we learned in the last project. Remember that to respond to a click event; we need to attach an event handler function to the element.

script.js
for (let index = 0; index < openModalButtons.length; index++)
  openModalButtons[index].addEventListener('click', function () {
    console.log(openModalButtons[index].textContent);
  });

Now, if you save everything and go back to your browser, you will see that if you now click on a button, its text content is being logged to the console.

script.js
for (let index = 0; index < openModalButtons.length; index++)
  console.log(openModalButtons[index].textContent);

Great! But now, of course, this is not the only thing that we want to do. Instead, we want to display the modal and remember that it is already in the HTML file, which is hidden right now because it has the hidden class. If it did not have the hidden class, it would be visible.

This is exactly what we will do using JavaScript, .i.e. removing the hidden class. So what we want to do is take the modal and read its classList property which will give us the list of classes attached to the modal element. The classList property has a couple of methods, and the one we are interested in is the remove method which takes as parameter the class or classes we want to remove. In our case, the hidden class.

script.js
for (let index = 0; index < openModalButtons.length; index++)
  openModalButtons[index].addEventListener('click', function () {
    modal.classList.remove('hidden');
  });

Just remember that we do not use the dot (.) here when specifying the class we want to remove.

Besides removing classes, we can also add classes, and we can even check if an element contains a certain class or not. We will use all of them in this lecture and the next one.

Now, if you save everything and try to click on one of the buttons, you will see that the modal appears, which means that the hidden class is gone! We can't, of course, close it because that functionality is not yet there.

You might have noticed that our overlay isn't present, and that's because it still has the hidden class. In order to show that overlay, we also need to remove the hidden class from that element.

script.js
for (let index = 0; index < openModalButtons.length; index++)
  openModalButtons[index].addEventListener('click', function () {
    modal.classList.remove('hidden');
    overlay.classList.remove('hidden');
  });

If you now save everything, it should look great in the browser! Using classes like this is a really great and handy way of manipulating web pages, and in practice, adding and removing classes like we just did here is the main way we change styles on websites. That's because a class basically aggregates many styles in just one class. In this case, the hidden class only contains this one style (display: none).

This would've been the same as specifying the display style directly in JavaScript. That is, we could've also done:

script.js
modal.style.display = 'block';

As mentioned, the above code will do the same thing, but you can see that it's a bit more of a pain because then we would have to memorize what exactly we have to write, .i.e. property and value

script.js
modal.style.property = value;

This is only for one style but imagine that the hidden class actually had ten properties. Then we would have to write all of these properties here manually and change all their values. So that's a lot of work, and it's not fun.

Instead, we can aggregate all of these properties in one class, that we then define in our CSS, and then in JavaScript, we add and remove these classes as we need or don't need these styles.

Usually, when you need to manipulate styles on a page, always export the styles into a class and then use the class just like we did.

Now what we just did here is showing the modal window. But now, let's also add the ability to hide it by clicking the X button because it's not possible to close the window unless we reload the whole page.

script.js
closeModalButton.addEventListener('click', function() {
    // Add some action
})

So again, we are just defining the function here. We are not calling it. The JavaScript engine will call this function as soon as the click event happens on the close button element.

In the close button event handler, we want to do the opposite. That is, we want to add the hidden class back, which will then hide the modal. And the same for the overlay.

script.js
modal.classList.add('hidden');
overlay.classList.add('hidden');

Save everything, and it should now work as expected. We are now able to open and close our modal. It should work for any of the three buttons.

Next up, we want to close the modal whenever we click outside of it. So basically we want the above code we just wrote in our close button event handler also to be executed when we click on the overlay.

script.js
overlay.addEventListener('click', function () {
  modal.classList.add('hidden');
  overlay.classList.add('hidden');
});

Now everything works great. But I guess you've already found some duplicates, and of course, that's not a good idea because doing that does not respect the DRY principle.

To fix this duplicate issue, we want the same function to be executed whether we click the close button or click the overlay.

So what we can do is simply create a function expression called closeModal.

script.js
const closeModal = function () {
  modal.classList.add('hidden');
  overlay.classList.add('hidden');
};

Then, assign this function to overlay and closeModalButton

script.js
closeModalButton.addEventListener('click', closeModal);

overlay.addEventListener('click', closeModal);

Again, notice that we are not calling the function. If we did something like this:

script.js
closeModalButton.addEventListener('click', closeModal());

Then it would not work because JavaScript will immediately call the function as soon as it executes the above line. And that's not what we want. We want the closeModal function to be executed only when the click event happens on the close modal button.

If you now save everything, it should still work as expected.

You can see that our code looks so much better than what we had before. Not only did we make our code more dry, but we also made it more readable, more expressive, and more declarative. So right now, when we read our code, it's a lot easier to understand.

To finish, we can also do the same thing for our buttons. That is, we create a function expression and then assign it to our event handler.

script.js
const openModal = function () {
  modal.classList.remove('hidden');
  overlay.classList.remove('hidden');
};

for (let index = 0; index < openModalButtons.length; index++)
  openModalButtons[index].addEventListener('click', openModal);