DEV Community

Abhi Jain
Abhi Jain

Posted on

All about Web events

Events are actions or occurrences that happen in the system you are programming, which the system tells you about so your code can react to them.

JavaScript's interaction with HTML is handled through events that occur when the user or the browser manipulates a page.

Events are a part of the Document Object Model (DOM) and every HTML element contains a set of events which can trigger JavaScript Code.

For example:

  • loading of a webpage
  • clicking on the button
  • hover on some content

Event Listeners
To react to these events, event handlers/listeners are used. A block of code is run when these events are fired.

The best practice for adding an event handler is the addEventListener() method. It can be used in scales with growing complexity in programs.

Event Objects
Look at the code snippet below, Most web developers might encounter the e/evt/event parameter in the function. Yes, this parameter is called the event object. It is automatically passed to event handlers to provide extra features and information.

const btn = document.querySelector('button');

function bgChange(e) {
  const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
  e.target.style.backgroundColor = rndCol;
  console.log(e);
}

btn.addEventListener('click', bgChange);
Enter fullscreen mode Exit fullscreen mode

Understanding the Event Flow

Let's find out how an event is propagated in the DOM.

Usually, an event is propagated towards the target element starting from its parents, and then it will propagate back towards its parent element.

Image description

Deep Dive Below
There are 3 phases in JavaScript event -

  • Capture Phase: The event is being propagated from the ancestor toward the parent.
    Window>Document>HTMLElement>target's parent

  • Target Phase: The event has arrived at the target

  • Bubble Phase: It is the reverse of the capture. It propagates from the target towards the ancestor until the window.
    target's parent>HTMLElement>Document>Window

The diagram below will give you a better understanding of event propagation

Image description

Lets visualize the event propagation with the help of an example

If you have a button element in the order given below.

Window>Document>DIV2>DIV1>button

Event Capturing

As you can see below, the click event triggered first in Window then Document then Parent then target. Propagation will stop at the target

Image description

Events can be registered for the capture phase by passing true in addEventListener, as shown below

window.addEventListener("click", () => {
    console.log('Window');
  },true);

document.addEventListener("click", () => {
    console.log('Document');
  },true);

document.querySelector(".div2").addEventListener("click", () => { 
    console.log('DIV 2');
  },true);

document.querySelector(".div1").addEventListener("click", () => {
    console.log('DIV 1');
  },true);

document.querySelector("button").addEventListener("click", () => {
    console.log('CLICK ME!');
  },true);
Enter fullscreen mode Exit fullscreen mode

Event Bubbling
You can see below, that the click event triggered first at the target and then propagated up till Window.

Image description

Events can be registered for the bubbling phase by passing false in addEventListener or don't pass anything as it is the default behaviour, as shown below

window.addEventListener("click", () => {
    console.log('Window');
  });

document.addEventListener("click", () => {
    console.log('Document');
  });

document.querySelector(".div2").addEventListener("click", () => { 
    console.log('DIV 2');
  });

document.querySelector(".div1").addEventListener("click", () => {
    console.log('DIV 1');
  });

document.querySelector("button").addEventListener("click", () => {
    console.log('CLICK ME!');
  });
Enter fullscreen mode Exit fullscreen mode

Now let's see, how to manipulate the phases on different objects to register for both phases

window.addEventListener("click", () => {
    console.log('Window');
  },true); //registered for capturing

document.addEventListener("click", () => {
    console.log('Document');
  }); //registered for bubbling

document.querySelector(".div2").addEventListener("click", () => { 
    console.log('DIV 2');
  }); //registered for bubbling

document.querySelector(".div1").addEventListener("click", () => {
    console.log('DIV 1');
  },true); //registered for capturing

document.querySelector("button").addEventListener("click", () => {
    console.log('CLICK ME!');
  },true); //registered for capturing
Enter fullscreen mode Exit fullscreen mode

Now the event will be triggered in order

Window>DIV1>CLICK ME in the Capture phase then
DIV2>Document in the Bubble phase
Refer to the Gif below for a visualisation

Image description

But what if we don't want to propagate event

Event object has a method available on it called stopPropagation(). When invoked on the event handler, stops propagation any further up the chain.

btn.addEventListener('click', e => {
  e.stopPropagation();
});
Enter fullscreen mode Exit fullscreen mode

Event Delegation
When we want some code to run when the user interacts with any one of a large number of child elements, we set the event listener on their parent and have events that happen on them bubble up to their parent rather than having to set the event listener on every child individually.

Lets see an example-

<ul id="container">
  <li id="mobile"></li>
  <li id="cameras"></li>
  <li id="shoes"></li>
  <li id="mobile"></li>
</ul>
Enter fullscreen mode Exit fullscreen mode

In the code above, there's a parent element with id="container" and multiple child elements. Now we want to console.log(id) of child elements on click event.

1st Approach that comes to mind is to addEventListener() on every child but if the list of child elements is too long then an event listener on every element might increase complexity.

const container = document.querySelector('#container');

container.addEventListener('click', (e)=>{
  if(e.target.tagname=="LI"){
    console.log(e.target.id);
  }
});
Enter fullscreen mode Exit fullscreen mode

Another approach could be to add an event listener to the parent container and use event bubbling to listen to the event and trigger the function. See the implementation above.

Conclusion

These concepts can help you to stop any unexpected event from firing. Moreover, it can help in performance optimization and avoid bottlenecks in the application.

Top comments (0)