All about Web events
Published: 2022-06-09 20:32:40

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);
```
**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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0slgvgq4m02vpy641cd7.jpg)

**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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jul40va3atrhytu7db5r.jpeg)

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](https://miro.medium.com/max/1400/1*bwNxfZVJ28WSAQ5s1MCc3A.gif)

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);
```

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

![Image description](https://miro.medium.com/max/1400/1*sfTTnB76jtG7dhfMQa0Zsg.gif)

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!');
  });
```

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
```
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](https://miro.medium.com/max/1400/1*L53X6yq5t-Nw_vl1EH9EWA.gif)

**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();
});
```
**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>
```

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);
  }
});
```

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.