Skip to content

Event Handling

When to Use

Use when responding to user interactions (clicks, input changes, scrolling) or custom application events.

Decision

Use vanilla JavaScript addEventListener for modern code. Implement event delegation for dynamic content. Use debounce/throttle for performance-intensive events. Ensure accessibility with keyboard events.

Pattern

Standard event handling:

Drupal.behaviors.eventHandling = {
  attach(context) {
    once('events', '.button', context).forEach(function (element) {
      element.addEventListener('click', function (event) {
        event.preventDefault();
        // Handle click
      });
    });
  }
};

Event delegation (for dynamic content):

// Instead of binding to individual elements
// Bind to parent, filter by selector
document.addEventListener('click', function (event) {
  if (event.target.matches('.dynamic-element')) {
    // Handle click on dynamically-added elements
    handleClick(event.target);
  }
});

Debounced events (performance):

// Dependency: core/drupal.debounce
const handleInput = Drupal.debounce(function (event) {
  // Expensive operation (search, validation)
  // Runs max once per 300ms
}, 300);

element.addEventListener('input', handleInput);

Keyboard accessibility:

element.addEventListener('keydown', function (event) {
  // Enter or Space activates like click
  if (event.key === 'Enter' || event.key === ' ') {
    event.preventDefault();
    element.click();
  }
});

Custom events:

// Dispatch custom event
const customEvent = new CustomEvent('drupal:moduleEvent', {
  detail: {data: 'value'},
  bubbles: true
});
element.dispatchEvent(customEvent);

// Listen for custom event
document.addEventListener('drupal:moduleEvent', function (event) {
  console.log(event.detail.data);
});

Common Mistakes

  • Wrong: No debounce on scroll/resize/input → Right: Debounce high-frequency events
  • Why: Executes hundreds of times per second, freezes UI
  • Wrong: Binding events without once() → Right: Wrap with once() in behaviors
  • Why: Duplicate bindings on AJAX updates, memory leaks
  • Wrong: Missing keyboard handling → Right: Support keyboard activation
  • Why: Breaks accessibility, fails WCAG compliance
  • Wrong: Using jQuery .on() unnecessarily → Right: Use addEventListener
  • Why: addEventListener is native, faster, no jQuery dependency
  • Wrong: Not removing listeners in detach() → Right: Clean up in detach()
  • Why: Memory leaks, ghost event handlers

See Also