How to make JavaScript Event Listeners less repetitive?

javascript events
javascript event listener list
remove event listener
javascript get event listeners
addeventlistener multiple events
javascript form events
event handler vs event listener javascript
event.data javascript

I am having some trouble thinking new ways to optimize this block of code. Now it looks too repetitive and long. I can write functions separately from EventListener but it would only make more lines of code and make it even longer.

let modalIntro = document.getElementById('modal-intro');
let buttonIntro = document.getElementById('button-intro');
let close = document.getElementsByClassName('close')[0];

buttonIntro.addEventListener ('click', function(){
    modalIntro.classList.remove("out");
    modalIntro.classList.add("in");
});

close.addEventListener('click', function (){
    modalIntro.classList.add("out");
});

let modalWork = document.getElementById('modal-work');
let buttonWork = document.getElementById('button-work');
let close1 = document.getElementsByClassName('close')[1];

buttonWork.addEventListener ('click', function(){
    modalWork.classList.remove("out");
    modalWork.classList.add("in");
});

close1.addEventListener('click', function (){
    modalWork.classList.add("out");
});

let modalAbout = document.getElementById('modal-about');
let buttonAbout = document.getElementById('button-about');
let close2 = document.getElementsByClassName('close')[2];

buttonAbout.addEventListener ('click', function(){
    modalAbout.classList.remove("out");
    modalAbout.classList.add("in");
});

close2.addEventListener('click', function (){
    modalAbout.classList.add("out");
});

let modalContact = document.getElementById('modal-contact');
let buttonContact = document.getElementById('button-contact');
let close3 = document.getElementsByClassName('close')[3];

buttonContact.addEventListener ('click', function(){
    modalContact.classList.remove("out");
    modalContact.classList.add("in");
});

close3.addEventListener('click', function (){
    modalContact.classList.add("out");
});

Any help would be much appreciated.


Seeing as you have a common naming pattern, and assuming the close button is child of each modal, you could do something like this:

Note I added modal.classList.remove('in'); to the close button

Solution

function bindModals(modals) {
  modals.forEach(name => {
    let modal = document.getElementById(`modal-${name}`)
    let button = document.getElementById(`button-${name}`)
    let close = modal.querySelector('.close');

    button.addEventListener ('click', function(){
      modal.classList.remove('out');
      modal.classList.add('in');
    });
    close.addEventListener('click', function (){
      modal.classList.remove('in');
      modal.classList.add('out');
    });
  });
}
bindModals(['intro', 'work', 'about', 'contact'])
.out {
  display: none;
}
.in {
  display: block;
  border: 1px solid red;
  height: 200px;
  width: 400px;
}
<section>
  <button id="button-intro">Intro</button>
  <button id="button-work">Work</button>
  <button id="button-about">About</button>
  <button id="button-contact">Contact</button>
</section>
<section id="modal-intro" class="out">
  <button class="close">Close</button>
  <p>Intro Modal</p>
</section>
<section id="modal-work" class="out">
  <button class="close">Close</button>
  <p>Work Modal</p>
</section>
<section id="modal-about" class="out">
  <button class="close">Close</button>
  <p>About Modal</p>
</section>
<section id="modal-contact" class="out">
  <button class="close">Close</button>
  <p>Contact Modal</p>
</section>

JavaScript Event Listeners: Delegation vs. Closures, How to add event listeners to a set of repeating DOM elements typically attached to dynamic DOM elements, making them much less stable. When using the addEventListener() method, the JavaScript is separated from the HTML markup, for better readability and allows you to add event listeners even when you do not control the HTML markup. You can easily remove an event listener by using the removeEventListener() method.


You can use a function to get element Id to make it more shorter:

function getElem(el) {
  return document.getElementById(el);
}

Now remove all below code:

let modalIntro = document.getElementById('modal-intro');
let buttonIntro = document.getElementById('button-intro');

let modalWork = document.getElementById('modal-work');
let buttonWork = document.getElementById('button-work');


let modalContact = document.getElementById('modal-contact');
let buttonContact = document.getElementById('button-contact');

– and replace variable with getElem('elem-id'), e.g given below

    getElem('modal-intro').classList.remove("out");
    getElem('modal-intro').classList.add("in");

Similarly you can make common function to get Element by class and add event listener.

Handling Events :: Eloquent JavaScript, Though we have ignored it so far, event handler functions are passed an argument: the event See Chapter 19 for a less primitive drawing program. createTextNode( "supercalifragilisticexpialidocious ".repeat(1000))); let bar = document. JavaScript Event Listeners. In this tutorial you will learn about DOM event listeners in JavaScript. Understanding Event Listeners. The event listeners are just like event handlers, except that you can assign as many event listeners as you like to a particular event on particular element.


Without having any HTML I am not sure if this is going to be perfectly accurate but I can try something.

I see each modal behaves in the same way... I would add an attribute to each button that indentifies which modal it activates, and a class so we can refer to all of them:

<input type="button" value="intro" class="modalButton" id="button-intro" data-activates="modal-intro">
<input type="button" value="work" class="modalButton" id="button-work" data-activates="modal-work">
<input type="button" value="about" class="modalButton" id="button-about" data-activates="modal-about">
<input type="button" value="contact" class="modalButton" id="button-contact" data-activates="modal-contact">

Now we can refer to each and action with this line of code:

document.getElementsByClassName("modalButton").forEach(function(btn) {
    btn.addEventListener ('click', function() {
        var modal = document.getElementById(btn.getAttribute("data-activates"));
        modal.classList.remove("out");
        modal.classList.add("in");
    });
});

Assuming the buttons are direclty inside the modal like this:

<!-- Example of a modal with a close button -->
<div id="modal-intro" class="modal out">
    <input type="button" class="close" value="close">
    <!-- content -->
</div>

We can get to the modal by moving up in the DOM tree:

document.getElementsByClassName("close").forEach(function(closeBtn) {
    closeBtn.addEventListener ('click', function(){
        // In this case the modal is the button's parent
        closeBtn.parentElement.classList().remove("in");
        closeBtn.parentElement.classList().add("out");
        // P.S I also suppose you want to remove the "in" class
    });
});

Obiouvsly, if the button is deeper inside the modal, you just need to call the 'parentElement' property till you get to it.

EventTarget.addEventListener(), One of the things I'm very impressed Ghostlab can do is sync the events from one to the event – that is, unless a lower event handler decides to stop propagation, In our example, we have to click event handlers registered on our element. And I thought, I have made a clear statement, but I will try to repeat some points:. In the parentheses, list the event listener but without the on. (In this example, the event listener is onclick , which is shortened to click .) You then add the function to run.


Here's another approach that uses the fact that you have the same structure times three. This would be much shorter if I had used jQuery, but I kept it vanilla since you don't seem to be using it:

document.querySelectorAll("nav a").forEach(a => a.addEventListener("click", function(e) {
  e.preventDefault(); // don't navigate to href
  // hide all modals, i.e. remove the one potentially still open
  document.querySelectorAll("#modals > div").forEach(modal => modal.classList.remove("in"));
  // finally, show modal
  document.getElementById(this.dataset.modal).classList.add("in");
}));

document.querySelectorAll("#modals > div").forEach(modal =>
  // for each modal, grab its close button
  modal.querySelector(".close").addEventListener("click", function(e) {
    e.preventDefault(); // don't navigate to href
    modal.classList.remove("in"); // and hide the modal
  })
);
#modals>div {
  position: fixed;
  width: 50%;
  left: 25%;
  box-sizing: border-box;
  border: 1px solid black;
  padding: 0.5em;
  top: -300px;
  transition: .5s
}

#modals .close {
  float: right
}

.in {
  top: 50px !important;
}
<nav>
  <a data-modal="intro" href="">Intro</a>
  <a data-modal="work" href="">Work</a>
  <a data-modal="about" href="">About</a>
</nav>
<div id="modals">
  <div id="intro"><a href="" class="close">X</a>
    <p>Intro modal</p>
  </div>
  <div id="work"><a href="" class="close">X</a>
    <p>Work modal</p>
  </div>
  <div id="about"><a href="" class="close">X</a>
    <p>About modal</p>
  </div>
</div>

JavaScript Event Madness! Capturing *all* events without , You don't want to create an event listener for each element. To kick your JavaScript skills into outer space, everything you see here and To echo what I mentioned in the intro, the obvious reason is that you don't want to duplicate code​. you'll end up having to use everything you saw here at least once. Now what we did in line 2 is, we tied an Event Listener with our button object. We passed 2 parameters, one name of the event in our case 'click' and other is a callback function and we used an arrow function as a parameter of the and in arrow function, we have event parameter (though we can call it anything we want whether just e or myEvent).


Easiest would be to simply create a function:

function MakeModal(modalId, buttonId, closeId)
    let modal = document.getElementById(modalId);
    let button = document.getElementById(buttonId);
    let close = document.getElementsById(closeId);

    button.addEventListener ('click', function(){
        modal.classList.remove("out");
        modal.classList.add("in");
    });

    close.addEventListener('click', function (){
        modal.classList.add("out");
    });
}

Which you can then invoke:

MakeModal('modal-intro', 'button-into', 'close-intro');
MakeModal('modal-about', 'button-about', 'close-about');
MakeModal('modal-contact', 'button-contact', 'close-contact');

That's just programming basics: DRY.

Do note that you need to add ID's to your close-buttons instead of classes (or you can rewrite it to let close = modal.find('.close'); or something and in which case you can get rid of the third argument closeId). And if you adhere strictly to the naming convention used here you could even simplify the multiple MakeModal(...) calls:

['intro', 'about', 'contact'].forEach(function(e) { 
    MakeModal('modal-' + e, 'button-' + e, 'close-' + e); 
});

Or, if you got rid of the third argument and used the .find(...) suggestion:

['intro', 'about', 'contact'].forEach(function(e) { 
    MakeModal('modal-' + e, 'button-' + e); 
});

I'm no jQuery expert by any means but if I'm not mistaken

modal.classList.remove("out");
modal.classList.add("in");

can be written as:

modal.classList.switchClass("out", "in");

Handling Events for Many Elements, Some DOM elements are queried for and event listeners are being right in the parameter list, so we don't have to repeat the event. part:. The latest addition to JavaScript event handlers are event listeners. An event listener watches for an event on an element. Instead of assigning the event directly to a property on the element, we will use the addEventListener() method to listen for the event.


Function identity in JavaScript, or how to remove event listeners , to use HTML, CSS, JavaScript, SQL, PHP, Python, Bootstrap, Java and XML. When clicking on a button, execute the first event handler, and stop the rest of the alert ("I will not get to say Hello World"); }. Try it Yourself ». Definition and Usage. The stopImmediatePropagation() method prevents other listeners of the same  In this JavaScript tutorial you will learn how to run multiple functions when a JavaScript event happens. I will show how to do this using two different methods, where one includes using


stopImmediatePropagation() Event Method, With the HTML DOM, JavaScript can get the elements from there and handle events. Some events We can add event handlers straight to an element's code. We can write There's less repeated code than the first example. Then its immediate parent also knows about the context, but a little bit less, and so on till the very top element that handles general concepts and runs the last. Bubbling and capturing lay the foundation for “event delegation” – an extremely powerful event handling pattern that we study in the next chapter.


Using Event Handlers in JavaScript, The idea is that if we have a lot of elements handled in a similar way, then In the handler we get event.target , see where the event actually Less code: when adding or removing elements, no need to add/remove handlers. Note: For event listeners attached to the event target, the event is in the target phase, rather than the capturing and bubbling phases. Events in the target phase will trigger all listeners on an element in the order they were registered, regardless of the useCapture parameter.