DEV Community

John Au-Yeung
John Au-Yeung

Posted on • Updated on

Introduction to JavaScript Event Handlers

Subscribe to my email list now at http://jauyeung.net/subscribe/

Follow me on Twitter at https://twitter.com/AuMayeung

Many more articles at https://medium.com/@hohanga

Any websites with dynamic functionality need to deal with users interacting with them. This is the pages have controls that users can use. To let users use those controls, the pages have to be able to handle the actions done by the users to the controls. These are example of events in JavaScript. Events also include other activities done by the browser like page loading.

With the HTML DOM, JavaScript can get the elements from there and handle events. Some events are divided into groups to what elements they’re applied to. Other events apply to all elements other than the body and frameset elements.

The list of events that are supported by all HTML elements are below:

  • abort, triggered when file load is aborted
  • change, triggered when value has changed
  • click, triggered when mouse is clicked or screen is tapped
  • dbclick, triggered when mouse is clicked twice
  • input , trigger when input value in an input or textarea element is changed
  • keydown, triggered when key is down
  • keyup, trigger when key is released after being pressed
  • mousedown, triggered when mouse button is down
  • mouseenter, triggered when mouse button is released
  • mouseleave, triggered when mouse leaves over an element
  • mousemove, triggered when mouse moves over an element
  • mouseout, triggered when mouse moves off an element
  • mouseover, triggered when mouse hovers over an element
  • mouseup, triggered when mouse button is released
  • mousewheel, triggered when mouse wheel is rotated
  • onreset , triggered when a form is reset
  • select, triggered when text is selected
  • submit, triggered when form is submitted
  • drag, triggered when element is being dragged
  • dragend, triggered when element is dragged
  • dragenter, triggered when dragged element enters the drop target
  • dragstart, triggered when element starts dragging
  • dragleave, triggered when dragged element leaves a valid drop target
  • dragover, triggered when element or text selection is dragged over a valid drop target
  • drop, triggered when element is dropped on a valid drop target

Below some HTML audio and video events:

  • pause, triggered when playback of media has started
  • play, triggered when playback has begun
  • playing, triggered when media is playing
  • seeking, triggered when media is being seeked
  • seeked, triggered when media is done seeking

Events supported by every element except the body and frameset elements are below:

  • blur, triggered when element loses focus
  • error, triggered when file failed to load
  • focus, triggered when element is in focus
  • load, triggered when a file and attached files are loading
  • resize, triggered when document is resized
  • scroll, triggered when element is scrolled

Events supported by the window object is below:

  • afterprint, triggered when print preview window has closed or document has started printing
  • beforeprint, triggered when print preview window is opened or document is going to print
  • beforeunload, triggered when the document is unloaded
  • hashchange, triggered when the part of the URL with the pound sign (#) is changed
  • pagehide, triggered when browser leaves a page in browser history
  • pageshow, triggered when browser goes to a page
  • popstate, triggered when session history item changes
  • unload, triggered when document and included files are being unloaded

There are many more events that can be handled by browsers. They are listed at https://developer.mozilla.org/en-US/docs/Web/Events.

Event Handling

When the browser’s JavaScript code is responding to those events, it’s called event handling. There are a few ways to handle events in JavaScript.

Inline Event Handlers

We can add event handlers straight to an element’s code. We can write code in HTML element’s attributes to handle code. For example, if we want to handle the event when a button is clicked we can put the event handling code in the onclick attribute of the button element, like so:

<button onclick='alert('Button clicked')>Click me</button>

It’s likely than we want to prevent the default action from happening, like navigating to a new page or triggering submit when a button is clicked. To prevent these default actions from happening, we can put return false at the end of the script. We can do following to prevent navigation to a new page if we handle the onclick event in an a tag:

<a onclick='alert('Clicked'); return false;'>Click me</a>

Handling Events in Script Tag

Inline event handlers aren’t good because you’re mixing dynamic code in HTML, which is supposed to only care about organizing pages into meaningful sections. Also, if you have complex code, then it’s very hard to put all the code in the attribute. We should attach event handlers to elements by getting them in dedicated code and then putting the code in the event handler’s callback function to handle the events.

For example, if we have code that increases and decrease the counter when you press different buttons, we can write the code as follows.

We make a new folder, then in index.html , we put:

<html>  
  <head>  
    <title>Counting App</title>  
  </head> 
  <body>  
    <h1>Click the button to count.</h1>  
    <p>Current Number: <span id="currentCount">0</span></p>  
    <button id="increaseButton">Increase</button>  
    <button id="decreaseButton">Decrease</button>  
    <script src="script.js"></script>  
  </body>  
</html>

Then in script.js , we put:

const handleIncrease = () => {  
  const currentCount = document.getElementById("currentCount");  
  const increaseButton = document.getElementById("increaseButton");  
  increaseButton.onclick = () => {  
    currentCount.innerHTML++;  
  };  
};

const handleDecrease = () => {  
  const currentCount = document.getElementById("currentCount");  
  const decreaseButton = document.getElementById("decreaseButton");  
  decreaseButton.onclick = () => {  
    currentCount.innerHTML--;  
  };  
};

const initialize = () => {  
  handleIncrease();  
  handleDecrease();  
};

window.onload = initialize;

In script.js, we have the functions to handle the clicks of the buttons in index.html. We attached the event handlers by getting the element object by using getElementById, then we set the onclick property of the elements with their own anonymous event handler functions. If we want to assign a function reference to it, we can do it like we did to window.onload. In the handlers we get the content of the element with IDcurrentCount, and then modified the innerHTML property to change the content after we got the original content. In the last line, we assign the initialize variable to it. We do not want to call it immediately, but rather when the event actually happened, so there’s no parenthesis after the initialize. We are assigning it the reference on the function, rather than calling it immediately and returning something then assigning the result to it.

addEventListener

Another way to attach an event handler to a function is to use the addEventListener function available with the HTML element object. The addEventListener event listens to DOM events that’s trigger and runs the callback function that you pass into it when the event happens. The callback function has one argument which contains the details of the event that was triggered. With addEventListener we can reuse code since we can use the same callback function in multiple places. You can also control how the event is triggered since we have the parameter which contains the event details. It also works on non-element nodes in the DOM tree.

If we change the above example to use addEventListener we can replace what we had in script.js with:

const handleClick = e => {  
  const currentCount = document.getElementById("currentCount");  
  if (e.target.id === "increaseButton") {  
    currentCount.innerHTML++;  
  } if (e.target.id === "decreaseButton") {  
    currentCount.innerHTML--;  
  }  
};

const handleIncrease = () => {  
  const increaseButton = document.getElementById("increaseButton");  
  increaseButton.addEventListener("click", handleClick);  
};

const handleDecrease = () => {  
  const decreaseButton = document.getElementById("decreaseButton");  
  decreaseButton.addEventListener("click", handleClick);  
};

const initialize = () => {  
  handleIncrease();  
  handleDecrease();  
};

window.onload = initialize;

As we can see, we used the same handleClick function for handling clicks of button the increaseButton and the decreaseButton. We did this by checking which element is triggering the click event by using the e.target.id property, which has the ID attribute of the element. Doing different actions in the same event handler function for different elements is called event delegation. We are delegating different actions as different elements are triggered. There’s less repeated code than the first example.

The addEventListener function takes 3 arguments. The first is the string that has the event name, the second is a callback function to handle the events. The last one indicates whether a parent element is also associated with the event. It’s true if it is and false otherwise.

If there’s a parent element that’s also associated with the event, then you can propagate the event to the parent element. It’s called bubbling up an event and the event will also be triggered on the parent element if it does. There’s also the capture method, which lets the parent element’s event happen first and the child after. It’s rarely used in code so we don’t have to be too concerned about this.

An example of event bubbling up would be as follows. If we have the following in index.html :

<html>  
  <head>  
    <title>Click App</title>  
  </head> 
  <body>  
    <div id="grandparent">  
      <div id="parent">  
        <button id="child">  
          Click Me  
        </button>  
      </div>  
    </div>  
    <script src="script.js"></script>  
  </body>  
</html>

and the following in script.js :

window.onload = () => {  
  const child = document.getElementById("child");  
  const parent = document.getElementById("parent");  
  const grandparent = document.getElementById("grandparent");  
  child.addEventListener(  
    "click",  
    () => {  
      alert("Child Clicked");  
    },  
    true  
  ); 

  parent.addEventListener(  
    "click",  
    () => {  
      alert("Parent Clicked");  
    },  
    true  
  ); 

  grandparent.addEventListener(  
    "click",  
    () => {  
      alert("Grandparent Clicked");  
    },  
    true  
  );  
};

Then when the button ‘Click me’ is clicked, we should get 3 alerts, since we specified to let the click events bubble up all the way to the top element.

Stop Event Propagation

If we only want the event to happen on the one you want, then you have to stop it from propagating to the parent. If we modify script.js from the previous example to:

window.onload = () => {  
  const child = document.getElementById("child");  
  const parent = document.getElementById("parent");  
  const grandparent = document.getElementById("grandparent");  
  child.addEventListener(  
    "click",  
    e => {  
      if (e.stopPropagation) {  
        e.stopPropagation();  
      }  
      alert("Child Clicked");  
    },  
    true  
  ); 

  parent.addEventListener(  
    "click",  
    e => {  
      if (e.stopPropagation) {  
        e.stopPropagation();  
      }  
      alert("Parent Clicked");  
    },  
    true  
  ); 

  grandparent.addEventListener(  
    "click",  
    e => {  
      if (e.stopPropagation) {  
        e.stopPropagation();  
      }  
      alert("Grandparent Clicked");  
    },  
    true  
  );  
};

We should only see ‘Grandparent Clicked’ since we stopped the event from propagating with the stopPropagation function. Stopping propagation should make your code faster since bubbling and capturing takes resources.

Top comments (4)

Collapse
 
drmaquino profile image
Mariano Aquino • Edited

Is there any priority or rule to know whether a click on a DOM element will trigger the most inner children or the most outer grandparent? And another question, how would you handle the case where you want only a specific element to handle a particular event? For instance, the parent, but not the child nor the grandparent.
Thanks!!

Collapse
 
aumayeung profile image
John Au-Yeung

Bubbling will go from child to parent and capturing is the other way around.

Collapse
 
milburngomes profile image
MilburnGomes

Great article, very clear and to the point - easy to understand. Thanks for sharing.

Collapse
 
aumayeung profile image
John Au-Yeung

Thanks so much for reading!