Skip to content
beginner Phase 6 · DOM

Event Listeners

Attach and remove event listeners with addEventListener and removeEventListener.

30m
0 problems
Topic Progress 0%

addEventListener

addEventListener attaches an event handler to an element.

Basic Usage

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

// Add click handler
button.addEventListener('click', () => {
  console.log('Button clicked!');
});

// Named function (easier to remove)
function handleClick() {
  console.log('Clicked!');
}
button.addEventListener('click', handleClick);

Event Types

// Mouse events
button.addEventListener('click', handler);
button.addEventListener('dblclick', handler);
button.addEventListener('mousedown', handler);
button.addEventListener('mouseup', handler);
button.addEventListener('mouseover', handler);

// Keyboard events
document.addEventListener('keydown', handler);
document.addEventListener('keyup', handler);

// Form events
form.addEventListener('submit', handler);
input.addEventListener('input', handler);
input.addEventListener('change', handler);
input.addEventListener('focus', handler);
input.addEventListener('blur', handler);

// Window events
window.addEventListener('resize', handler);
window.addEventListener('scroll', handler);
window.addEventListener('load', handler);

Adding Multiple Listeners

// Same event, multiple handlers
button.addEventListener('click', firstHandler);
button.addEventListener('click', secondHandler);

// Both will execute
button.addEventListener('mousedown', handler1);
button.addEventListener('mouseup', handler2);

Options Object

button.addEventListener('click', handler, {
  capture: false,  // Listen in capturing phase
  once: true,      // Remove after first call
  passive: true,   // Never calls preventDefault()
  signal: abortController.signal // AbortController
});

// once: true - auto-remove after first call
button.addEventListener('click', () => {
  console.log('This runs only once');
}, { once: true });

// passive: true - for performance (scroll, touch events)
document.addEventListener('touchmove', handler, { passive: true });

Arrow Functions vs Named Functions

// Arrow function - can't remove (no reference)
button.addEventListener('click', () => {
  console.log('clicked');
});

// Named function - can remove
function handleClick() {
  console.log('clicked');
}
button.addEventListener('click', handleClick);
button.removeEventListener('click', handleClick);

removeEventListener

removeEventListener removes an event handler. Must pass the same function reference.

Basic Usage

function handleClick() {
  console.log('clicked');
}

// Add
button.addEventListener('click', handleClick);

// Remove (must pass same function reference)
button.removeEventListener('click', handleClick);

Common Mistake

// WRONG - anonymous function can't be removed
button.addEventListener('click', () => {
  console.log('clicked');
});
button.removeEventListener('click', () => {
  console.log('clicked');
}); // Doesn't work! Different function!

// CORRECT - use named function
function handleClick() {
  console.log('clicked');
}
button.addEventListener('click', handleClick);
button.removeEventListener('click', handleClick); // Works!

Cleanup Pattern

function setup() {
  const handler = (e) => console.log(e.target);
  
  button.addEventListener('click', handler);
  
  // Return cleanup function
  return () => {
    button.removeEventListener('click', handler);
  };
}

const cleanup = setup();
// Later:
cleanup(); // Removes listener

AbortController Pattern

const controller = new AbortController();

button.addEventListener('click', handler, {
  signal: controller.signal
});

// Remove all listeners with this signal
controller.abort();

// Can also abort after timeout
setTimeout(() => controller.abort(), 5000);

Event Listener Cleanup in Components

// React-like cleanup
useEffect(() => {
  const handler = () => { /* ... */ };
  window.addEventListener('resize', handler);
  
  return () => {
    window.removeEventListener('resize', handler);
  };
}, []);

// Class component
class Component {
  componentDidMount() {
    window.addEventListener('resize', this.handleResize);
  }
  
  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize);
  }
}

Event Object

The event object contains information about the event.

Event Object Properties

button.addEventListener('click', (event) => {
  // Basic properties
  console.log(event.type);        // 'click'
  console.log(event.target);      // Element that triggered event
  console.log(event.currentTarget); // Element with listener
  console.log(event.timeStamp);   // When event occurred
  
  // Mouse properties
  console.log(event.clientX);     // X position
  console.log(event.clientY);     // Y position
  console.log(event.pageX);       // X relative to document
  console.log(event.pageY);       // Y relative to document
  
  // Keyboard properties
  console.log(event.key);         // 'Enter', 'a', etc.
  console.log(event.code);        // 'KeyA', 'Enter'
  console.log(event.altKey);      // Alt held
  console.log(event.ctrlKey);     // Ctrl held
  console.log(event.shiftKey);    // Shift held
  
  // Form properties
  console.log(event.target.value);
  console.log(event.target.checked);
});

Preventing Default Behavior

// Prevent form submission
form.addEventListener('submit', (e) => {
  e.preventDefault(); // Form won't submit
  // Handle with JavaScript
});

// Prevent link navigation
link.addEventListener('click', (e) => {
  e.preventDefault(); // Won't navigate
});

// Prevent context menu
document.addEventListener('contextmenu', (e) => {
  e.preventDefault(); // No right-click menu
});

Stopping Propagation

// Stop bubbling
document.addEventListener('click', (e) => {
  if (e.target.closest('.modal')) {
    e.stopPropagation(); // Won't bubble to document
  }
});

// Stop immediate propagation
button.addEventListener('click', (e) => {
  e.stopImmediatePropagation(); // Other listeners won't fire
  console.log('First handler');
});

button.addEventListener('click', () => {
  console.log('Second handler'); // Won't run
});

Event Delegation with Event Object

const list = document.querySelector('ul');

list.addEventListener('click', (e) => {
  // Find the list item
  const item = e.target.closest('li');
  
  // Check if click was on a list item
  if (item && list.contains(item)) {
    // Get item data
    const id = item.dataset.id;
    const text = item.textContent;
    
    // Handle click
    handleItemClick(id, text);
  }
});

Quiz

1. Why can't you remove an anonymous event listener?

Question 1 options

2. What does e.preventDefault() do?

Question 2 options

3. What is the difference between target and currentTarget?

Question 3 options

4. What does the { once: true } option do (specific to event listeners)?

Question 4 options

Flashcards

Question

How to add an event listener?

Answer

element.addEventListener('event', handler, options) - options include capture, once, passive, signal.

Question

How to remove an event listener?

Answer

element.removeEventListener('event', handler) - must pass the same function reference used in addEventListener.

Question

What is e.target vs e.currentTarget?

Answer

target: element that triggered the event. currentTarget: element with the listener attached.

Question

What does preventDefault() do?

Answer

Prevents the browser's default action (form submit, link navigation) but doesn't stop propagation.

Revision Notes

Key Takeaways

  • 1. addEventListener is the standard way to handle events
  • 2. You need the same function reference to remove listeners
  • 3. e.target is the clicked element, e.currentTarget has the listener
  • 4. preventDefault() stops browser default behavior
  • 5. Use { once: true } for one-time handlers

Interview Tips

  • Show how to properly add and remove event listeners
  • Explain the difference between target and currentTarget
  • Demonstrate event delegation patterns
  • Discuss when to use { passive: true } for performance

Cheat Sheet

Event Listeners Cheat Sheet

Adding Listeners

el.addEventListener('click', handler, options);

Options

  • capture: Listen in capturing phase
  • once: Remove after first call
  • passive: Can't call preventDefault()
  • signal: AbortController signal

Removing Listeners

el.removeEventListener('click', handler);

Event Object

  • e.target: Triggered element
  • e.currentTarget: Listener element
  • e.preventDefault(): Stop default behavior
  • e.stopPropagation(): Stop bubbling