Event Listeners — Complete Guide
In this tutorial, you will learn about Event Listeners. We cover key concepts, practical examples, and best practices to help you master this topic.
Event listeners enable JavaScript to respond to user interactions like clicks, key presses, and form submissions through the addEventListener method on DOM elements.
What You'll Learn
- How to attach and remove event listeners with addEventListener and removeEventListener
- The event object properties: target, currentTarget, type, preventDefault, stopPropagation
- The difference between event handler properties and addEventListener
- Event listener options: once, passive, capture
Why It Matters
Everything interactive on the web relies on events. Without event listeners, buttons do nothing, forms cannot be submitted, and pages are completely static. Mastering events is essential for building any interactive application.
Real-World Use
- A "Like" button sends an AJAX request when clicked
- A search input triggers live suggestions after the user stops typing
- A game responds to keyboard arrow keys for character movement
flowchart LR A[User Action] --> B[Event Fired] B --> C[Event Object Created] C --> D[Capture Phase] D --> E[Target Phase] E --> F[Bubble Phase] F --> G[Event Listener Fires] G --> H[Callback Function] H --> I[Respond to Event]
Adding Event Listeners
The addEventListener method attaches a function to be called when a specific event type occurs on the element.
// Basic click listener
const button = document.querySelector('.click-me');
button.addEventListener('click', function(event) {
console.log('Button was clicked!');
console.log('Event type:', event.type);
console.log('Target element:', event.target);
});
// Arrow function syntax
button.addEventListener('click', (e) => {
console.log('Clicked via arrow function');
});
// Named function (useful for removal later)
function handleClick(event) {
console.log('Named handler executed');
}
button.addEventListener('click', handleClick);
// button.removeEventListener('click', handleClick);
Expected output: When the button is clicked, the console shows three messages from the three handlers. Each handler receives the event object with type and target properties.
The Event Object
Every event listener receives an event object with useful properties and methods.
const link = document.querySelector('a');
link.addEventListener('click', function(event) {
// Prevent default behavior (navigation)
event.preventDefault();
console.log('Default prevented');
// Event properties
console.log('Event type:', event.type);
console.log('Target:', event.target);
console.log('Current target:', event.currentTarget);
console.log('Timestamp:', event.timeStamp);
console.log('Bubbles:', event.bubbles);
console.log('Cancelable:', event.cancelable);
// Coordinates for mouse events
if (event.clientX !== undefined) {
console.log('Mouse X:', event.clientX);
console.log('Mouse Y:', event.clientY);
}
});
Expected output: Clicking the link logs the event details. The default navigation is prevented — the browser does not follow the link.
Listener Options
The third argument to addEventListener can be an options object or a boolean (for capture phase).
const btn = document.querySelector('.once-btn');
// Option 1: Fire only once
btn.addEventListener('click', function() {
console.log('This runs only once');
}, { once: true });
// Option 2: Passive listener (performance hint for scroll)
window.addEventListener('touchstart', function(event) {
// Do not call preventDefault() inside passive listeners
console.log('Touch started');
}, { passive: true });
// Option 3: Capture phase
document.body.addEventListener('click', function(event) {
console.log('Body capture phase');
}, { capture: true });
// Equivalent shorthand for capture
// document.body.addEventListener('click', handler, true);
// Option 4: Abort signal (for cleanup)
const controller = new AbortController();
window.addEventListener('resize', function() {
console.log('Window resized');
}, { signal: controller.signal });
// Later: controller.abort(); removes the listener
Expected output: The once button fires only on the first click. The touch listener does not block scrolling. The body capture listener fires before the target phase.
Event Handler Properties
Before addEventListener, events were assigned via properties like onclick and onmouseover.
const button = document.querySelector('.old-style');
// Property assignment (only one handler at a time)
button.onclick = function(event) {
console.log('First handler');
};
// This overwrites the first handler
button.onclick = function(event) {
console.log('Second handler');
};
// Only "Second handler" will run
// addEventListener allows multiple handlers
button.addEventListener('click', function() {
console.log('Third handler');
});
button.addEventListener('click', function() {
console.log('Fourth handler');
});
// Both "Third handler" and "Fourth handler" run alongside the onclick
Expected output: Clicking the button runs only "Second handler" from the onclick property, plus both addEventListener handlers. The first onclick handler is overwritten.
Removing Event Listeners
To remove a listener, you must pass the exact same function reference to removeEventListener.
const button = document.querySelector('.toggle-btn');
function handleToggle(event) {
console.log('Toggle handler executed');
// Remove this listener after first use
button.removeEventListener('click', handleToggle);
console.log('Listener removed');
}
button.addEventListener('click', handleToggle);
// This WILL NOT work (different function reference)
button.removeEventListener('click', function() {
console.log('This does nothing');
});
// Anonymous functions cannot be removed
// Always use named functions if you need to remove them later
Expected output: The first click runs the handler and removes it. Subsequent clicks do nothing because the listener has been removed. The anonymous function passed to removeEventListener does not match any listener.
Dispatching Custom Events
You can create and dispatch your own events.
const element = document.querySelector('.custom-event-target');
// Create a custom event
const customEvent = new CustomEvent('userAction', {
detail: {
userId: 42,
action: 'login',
timestamp: Date.now()
},
bubbles: true,
cancelable: true
});
// Listen for the custom event
element.addEventListener('userAction', function(event) {
console.log('Custom event received!');
console.log('Detail:', event.detail);
console.log('User ID:', event.detail.userId);
console.log('Action:', event.detail.action);
});
// Dispatch the event
element.dispatchEvent(customEvent);
// Custom events can be listened on any element and bubble up
document.addEventListener('userAction', function(event) {
console.log('Caught by document (bubbling)');
});
Expected output: When the custom event is dispatched, both the element listener and the document listener fire. The detail object passes the custom data to all listeners.
Common Mistakes
- Adding the same listener twice — If you add the exact same function reference with addEventListener, it executes twice for each event. The listener is not deduplicated.
- Using anonymous functions with removeEventListener — Anonymous functions create a new reference each time. You cannot remove them. Always use named functions if removal is needed.
- Forgetting to prevent default on form submissions — Form submission events cause a page reload by default. Call
event.preventDefault()in the submit handler. - Adding listeners inside loops without closure handling — Loop variables captured by reference cause all listeners to see the last value. Use let or an IIFE to capture the correct value.
- Not cleaning up listeners on element removal — Removed elements with attached listeners can cause memory leaks if the listener closure references external variables.
Practice Questions
- What is the difference between addEventListener and onclick? addEventListener supports multiple handlers, options (once, passive), and capture phase. onclick only supports one handler and overwrites previous ones.
- How do you remove an event listener? Call removeEventListener with the same event type and function reference. The function must be a named function, not an anonymous one.
- What does the
onceoption do? It automatically removes the listener after the first invocation. Equivalent to calling removeEventListener inside the handler. - Challenge: Create a debounced event listener. When the user types in an input field, the callback should fire only after they stop typing for 300ms. Cancel the previous timeout on each keystroke.
FAQ
Mini Project
Build a custom dropdown menu. The dropdown toggle button shows/hides a menu list on click. Clicking outside the dropdown closes it. Use event listeners on the document to detect outside clicks. Use stopPropagation to prevent the dropdown click from triggering the document listener. Add keyboard support: Escape closes the dropdown, Arrow keys navigate items.
What's Next
Continue with Lesson 13: Event Bubbling and Capturing to understand how events propagate through the DOM tree in capture and bubble phases.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro