Maker.io main logo

An Introduction to JavaScript Event Handlers

88

2026-06-22 | By Maker.io Staff

Web interfaces are expected to respond to user inputs. Buttons should react to clicks, input fields should validate their content as users type, and menus should open and close when users interact with them. JavaScript event handlers make this kind of behavior possible, as they allow code to listen for specific actions and conditions and react accordingly. Read on to learn how JavaScript event handlers work, how to attach and remove them, and how to use them to build interactive web pages.

Image of An Introduction to JavaScript Event Handlers

What is a JavaScript Event Handler?

An event handler is a function that allows a script to react to user actions and changing conditions on a website. For that to work, the event handler function must be tied to the actions that should trigger it. The browser then automatically calls the handler function whenever necessary.

Most elements on a website expose events that can be tied to custom event handlers. Common examples include the document loaded event, the button click event, and keyboard-related events for input fields, for example, to detect when users start or stop typing.

In recent years, browsers started supporting more and more device-specific events. For example, users can change their OS theme, so that websites can adjust their style based on user preferences, such as switching to a dark theme.

Attaching Event Handlers in JavaScript

There are several ways to attach event handlers to HTML elements or the page itself. To begin with, event handlers can be attached to static elements that exist in the HTML, for example, the body tag:

Copy Code
<html>
    <head>
        <title>JavaScript Event Basics</title>
        <script src="handlers.js"></script>
    </head>
    <body onload="pageLoaded()" id="primarySection">
        <p>Hello, world!</p>
    </body>
</html>

This short snippet links the external JavaScript file with the name handlers.js. That file contains a function called pageLoaded. This function is referenced by the onload event on the body tag, meaning that it becomes the event handler that the browser calls when the website body finishes loading. In this example, the handler only prints a debug message:

Copy Code
function pageLoaded() {
    console.log("Page loaded!");
}

This approach works well for elements already present in the HTML code. However, it does not work for ones created dynamically with JavaScript. In those cases, event handlers can be attached and removed during runtime, which works for both existing elements and those generated later.

JavaScript offers two ways to dynamically add events to HTML elements. The first approach is to directly assign a function to the element’s event property, for example:

Copy Code
function pageLoaded() {
    const container = document.getElementById("primarySection");
    const newButton = document.createElement("button");
    newButton.id = "generatedButton";
    newButton.textContent = "Click me!";
    newButton.onclick = buttonPressed; // Attach the new event
    container.appendChild(newButton);
}

function buttonPressed(eventData) {
    /* Handle the event */
}

In this example, the pageLoaded function from before is extended to generate a new button and dynamically add it to the website. However, before adding the new button to the container, the function assigns the buttonPressed handler to the new button’s onclick property, which links the click event to the handler. Setting the onclick property back to null removes the handler:

Copy Code
const btn = document.createElement("generatedButton");
btn.onclick = null;

Despite its simplicity, this approach should generally be avoided because assigning a new handler replaces any existing one and limits the element to a single handler.

Event handlers should instead be added and removed using the addEventListener and detachEventListener functions:

Copy Code
const newButton = document.createElement("button");
newButton.addEventListener("click", buttonPressed); // Register a click event handler
newButton.removeEventListener("click", buttonPressed); // Remove the click event handler

Using the specialized functions supports adding multiple handlers and doesn’t interfere with event propagation. However, removeEventListener does not work with anonymous functions, since it must always be called with the same reference that was used when registering an event handler:

Copy Code
const newButton = document.createElement("button");
newButton.addEventListener("click", function() {
    console.log("this handler is unremovable!") 
});

Anonymous event handlers can still be removed by setting the HTML element’s event parameter to null. However, doing so removes all event handlers, which might break functionality.

An AbortController can be used to detach multiple event handlers in one go by linking them to the same signal. Calling the controller’s abort function removes all linked handlers at once:

Copy Code
const controller = new AbortController();

window.addEventListener("resize", handleResize, { signal: controller.signal });
document.addEventListener("keydown", handleKeyPress, { signal: controller.signal });
btn.addEventListener("click", handleClick, { signal: controller.signal });

// Once done: remove the three handlers without affecting others
controller.abort();

This approach can improve code readability when several handlers should only be active temporarily, for example, while a modal window is open. Instead of removing each listener individually, they can be cleaned up with a single call without affecting unrelated handlers. Furthermore, it also allows developers to selectively remove anonymous handlers.

Accessing Event Details with the Event Object

Some events, such as click and keyboard events, generate additional data that can be useful when handling them. Whenever such an event occurs, the browser passes the data into the linked event handler. Custom code can access the additional data using a function parameter:

Copy Code
function buttonPressed(eventData) {
    console.log("Button pressed!");
    console.log(eventData);
}

This object contains additional information on the event, the element that triggered it, and event-specific data, for example, the keyboard key or the mouse position:

Image of Javascript ConsolePrinting the event object to the console shows the additional context it can provide to custom event handlers.

It further allows altering the event propagation chain, for example, by stopping events from propagating to the next element in the chain.

Event Propagation in JavaScript

An event not only affects the element where it originated. Instead, JavaScript events propagate through the entire HTML document tree from the outermost parent (usually the page itself) down to the innermost child (the element that was interacted with) and back up again. This process is called event propagation, and it includes three phases.

In the capture phase, the browser walks down the tree until it reaches the element that triggered the event. At that point, it enters the target phase. From there, it moves back up through the tree during the bubbling phase.

This flow chart explains event propagation in JavaScript.

This process lets parents react to events that originate from the children, which can help reduce the number of required event handlers. For example, instead of adding click handlers to every entry in a list, you can attach one handler to the list itself and handle clicks on any item as the event bubbles up. If needed, event bubbling can be stopped in a handler:

Copy Code
function handler(event) {
    // Do not inform parents of the event
    event.stopPropagation();
}

Conclusion

Users expect modern websites to be interactive. JavaScript events enable websites to react to user inputs and changing conditions. Each time an event fires, the browsers call all linked event handler functions. These functions implement custom behavior that allows websites to be interactive.

JavaScript event handlers can be added directly in HTML, which works well for static elements. They can also be added dynamically from within the script itself, which is the only way to add event handlers to dynamically generated elements. Although it is possible to assign event handlers directly to an element’s event properties, it’s recommended to use the special addEventListener and removeEventListener functions. These functions support adding multiple listeners and removing existing ones individually, without interfering with JavaScript event propagation.

Events in JavaScript do not exclusively affect the element that triggers them. Instead, events propagate down from the outermost parent during the capture phase. They reach the element where the event originated in the target phase, where they can be handled by the target’s listener. The event then bubbles back up to the outermost parent during the bubbling phase. This event propagation chain helps save listeners, as parents can react to changes in any of their children, instead of each child having to react in isolation.

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.