Master Javascript Events, Event Delegation and More…

How does a web application know when something happens on your DoM? The answer is straightforward: “Events”. But the correct question to ask may be, do we know enough about the events..? Answer to this might differ from person to person.Let’s Master Javascript Events and Event Delegation in this article.

What is an Event?

Event in general is any action triggered by the user or anyother API of the browser like user clicks, scroll, type something on keyboard or some printer showing a task completion. Events are not just triggers but also carry information such as the elment involved, it’s properties, type of event.

How to capture Events?

There are two well-known ways to capture event:

  • By adding a listener to element using “addEventListener” method.
const signupBtn = document.getElementById("signUp");
  console.log("Clicked Signup button");
  • By using element level event properties like onclick, onsubmit,…
<button onclick="saveData()">Save</button>

// Javascript
const saveData = ()=>{
  console.log("Saved Data");

There are a few more ways, but everything is just some working intelligent way of using these two ways. Why don’t you comment on your ways of handling events?

Event handlers seem very simple, but are they..? Just imagine building a calendar element with around 30+ dynamically rendered clickable elements, and all those will perform the same action. If we add event listeners for each component, we will enter into a maintenance hell and difficult to understand. The beauty of Javascript is for every problem, the solution is already available “Event Delegation”.

Event delegation

Event delegation offers a performant and maintainable approach to handle events. Instead of attaching listeners to individual element, we’ll add a single listener at a common ancestor. And this ancestor will act as single source for capturing event bubbles from decendants.

Event Bubbling: Events in the DOM "bubble up" the hierarchy, propagating from the target element to its ancestors. We can even stop the event propagation.
How It Works
  • Attach a single event listener to a common ancestor element.
  • When an event occurs on a descendant element, it bubbles up to the ancestor.
  • The ancestor’s event listener checks if the target element matches the criteria.
  • If it matches, the event listener executes the desired logic.
const listContainer = document.querySelector('ul');
listContainer.addEventListener('click', function(event) {
  if ( == 'LI') {
    console.log('Clicked list item:',;

In the above example, we listen to clicks on items in a list using Event Delegation.

Benefits of Event Delegation
  • Performance: Reduced number of event listeners improves memory usage and rendering speed.
  • Dynamic elements: New elements added to the DOM are automatically covered by the ancestor listener.
  • Maintainability: Code becomes cleaner and easier to manage, with event logic centralized in one place.

Master Javascript Events and Memory Leaks

Events are essential for dynamic and interactive web apps, but they may cause performance issues and memory leaks if not managed carefully.

How Events Can Cause Memory Leaks?
  • Event Listener Accumulation: Make sure to properly remove event listeners, as improper removal can create dangling references and occupy memory unnecessarily.
function createButton() {
  const button = document.createElement('button');
  button.addEventListener('click', () => {
    console.log('Button clicked!');
  return button;

// Create and remove a button:
const myButton = createButton();
document.body.removeChild(myButton); // Listener still exists in memory!
  • Closures and Circular References: When we use event listeners, we may require certain variables from the surrounding code. However, sometimes these variables can get linked together unintentionally, creating what is called “circular references“. This can lead to garbage collection being prevented, which in turn can cause memory leaks and harm the performance of our code. Hence, it is crucial to exercise caution when using closures in event listeners and to avoid creating circular references as much as possible.
function createList() {
  const hugeArray = new Array(100000).fill('x'); // Large array
  const list = document.createElement('ul');
  list.addEventListener('click', function() {
    console.log(hugeArray); // Huge array is kept in memory
  return list;
  • Global Event Listeners: Attaching event listeners directly to the window or document objects can persist even after other parts of the application are unloaded. This can lead to memory leaks if not managed correctly.
Best practices to prevent memory leaks:
  • Remove Event Listeners When Done
button.addEventListener('click', handleClick);
// Later, when the button is no longer needed:
button.removeEventListener('click', handleClick);
  • Use Weak References for Closures
const weakReference = new WeakRef(hugeArray);
list.addEventListener('click', function() {
  if (weakReference.deref()) {
    console.log(weakReference.deref()); // Access array only if still exists
  • Be Mindful of Global Listeners: Attaching listeners to window or document can retain references to elements and functions even after they’re removed from the DOM.
  • Consider Event Delegation: As explained above, event delegation can reduce the number of listeners and mitigate memory leak risks.


Event handling and event delegation are essential tools for building dynamic and efficient web applications. By understanding their concepts, benefits, and best practices, you can create more responsive, maintainable, and user-friendly web experiences

There are two more things I want to cover about events in the upcoming articles: Anatomy of Event Listener and Custom Events.

Let me know in the comments how you leverage the events to make your application interactive and user-friendly.

Share this article
Shareable URL
Leave a Reply

Your email address will not be published. Required fields are marked *

Read next
Subscribe to our newsletter