Events

Events are actions or occurrences that happen in the browser that JavaScript can respond to. They allow you to create interactive web applications by responding to user actions and other browser events.

Event Types

Mouse Events

element.addEventListener('click', function(event) {
  console.log('Element clicked');
});

element.addEventListener('dblclick', function(event) {
  console.log('Element double-clicked');
});

element.addEventListener('mousedown', function(event) {
  console.log('Mouse button pressed');
});

element.addEventListener('mouseup', function(event) {
  console.log('Mouse button released');
});

element.addEventListener('mousemove', function(event) {
  console.log('Mouse moved:', event.clientX, event.clientY);
});

element.addEventListener('mouseenter', function(event) {
  console.log('Mouse entered element');
});

element.addEventListener('mouseleave', function(event) {
  console.log('Mouse left element');
});

element.addEventListener('contextmenu', function(event) {
  event.preventDefault(); // Prevent right-click menu
  console.log('Right-click detected');
});

Keyboard Events

input.addEventListener('keydown', function(event) {
  console.log('Key pressed:', event.key);
  console.log('Key code:', event.keyCode); // Deprecated, use key instead
});

input.addEventListener('keyup', function(event) {
  console.log('Key released:', event.key);
});

input.addEventListener('keypress', function(event) {
  console.log('Key pressed and released');
});

// Special keys
document.addEventListener('keydown', function(event) {
  if (event.key === 'Enter') {
    console.log('Enter pressed');
  }

  if (event.ctrlKey && event.key === 's') {
    event.preventDefault();
    console.log('Ctrl+S pressed');
  }
});

Form Events

let input = document.querySelector('input');

input.addEventListener('focus', function(event) {
  console.log('Input focused');
});

input.addEventListener('blur', function(event) {
  console.log('Input lost focus');
});

input.addEventListener('change', function(event) {
  console.log('Value changed:', event.target.value);
});

input.addEventListener('input', function(event) {
  console.log('Input value changed:', event.target.value);
});

let form = document.querySelector('form');
form.addEventListener('submit', function(event) {
  event.preventDefault();
  console.log('Form submitted');
});

form.addEventListener('reset', function(event) {
  console.log('Form reset');
});

Window Events

window.addEventListener('load', function(event) {
  console.log('Page fully loaded');
});

window.addEventListener('resize', function(event) {
  console.log('Window resized:', window.innerWidth, window.innerHeight);
});

window.addEventListener('scroll', function(event) {
  console.log('Page scrolled:', window.scrollY);
});

window.addEventListener('beforeunload', function(event) {
  // Show confirmation dialog
  event.returnValue = 'Are you sure you want to leave?';
});

Document Events

document.addEventListener('DOMContentLoaded', function(event) {
  console.log('DOM fully loaded and parsed');
});

document.addEventListener('readystatechange', function(event) {
  console.log('Document ready state:', document.readyState);
});

Event Object

Every event handler receives an event object with information about the event:

element.addEventListener('click', function(event) {
  // Prevent default behavior
  event.preventDefault();

  // Stop event bubbling
  event.stopPropagation();

  // Event properties
  console.log('Event type:', event.type);
  console.log('Target element:', event.target);
  console.log('Current target:', event.currentTarget);

  // Mouse coordinates
  console.log('Mouse X:', event.clientX);
  console.log('Mouse Y:', event.clientY);
  console.log('Page X:', event.pageX);
  console.log('Page Y:', event.pageY);

  // Keyboard properties
  console.log('Key pressed:', event.key);
  console.log('Key code:', event.keyCode);
  console.log('Alt pressed:', event.altKey);
  console.log('Ctrl pressed:', event.ctrlKey);
  console.log('Shift pressed:', event.shiftKey);
  console.log('Meta pressed:', event.metaKey);
});

Event Propagation

Events propagate through the DOM in three phases:

Capturing Phase

Event travels from root to target element:

document.addEventListener('click', function() {
  console.log('Document (capturing)');
}, true); // true = capturing phase

parent.addEventListener('click', function() {
  console.log('Parent (capturing)');
}, true);

Target Phase

Event reaches the target element.

Bubbling Phase

Event bubbles up from target to root:

child.addEventListener('click', function() {
  console.log('Child (bubbling)');
}); // Default is bubbling

parent.addEventListener('click', function() {
  console.log('Parent (bubbling)');
});

document.addEventListener('click', function() {
  console.log('Document (bubbling)');
});

Stopping Propagation

child.addEventListener('click', function(event) {
  event.stopPropagation(); // Stop bubbling
  console.log('Child clicked, propagation stopped');
});

Event Delegation

Attach event listeners to parent elements to handle events on child elements:

// Instead of adding listeners to each li
document.querySelector('#list').addEventListener('click', function(event) {
  if (event.target.tagName === 'LI') {
    console.log('List item clicked:', event.target.textContent);
    event.target.classList.toggle('selected');
  }
});

// Works for dynamically added elements too
function addListItem(text) {
  let li = document.createElement('li');
  li.textContent = text;
  document.querySelector('#list').appendChild(li);
}

Custom Events

Create and dispatch your own events:

// Create custom event
let customEvent = new CustomEvent('userAction', {
  detail: { action: 'login', userId: 123 },
  bubbles: true,
  cancelable: true
});

// Dispatch event
element.dispatchEvent(customEvent);

// Listen for custom event
element.addEventListener('userAction', function(event) {
  console.log('User action:', event.detail.action);
  console.log('User ID:', event.detail.userId);
});

Event Listeners

Adding Event Listeners

// Method 1: addEventListener (recommended)
element.addEventListener('click', handleClick);
element.addEventListener('click', handleClick2); // Multiple listeners

// Method 2: Property assignment (only one listener)
element.onclick = handleClick;

// Method 3: Inline HTML (not recommended)
// <button onclick="handleClick()">Click me</button>

Removing Event Listeners

function handleClick() {
  console.log('Clicked!');
}

// Add listener
element.addEventListener('click', handleClick);

// Remove specific listener
element.removeEventListener('click', handleClick);

// For anonymous functions, you need a reference
let handler = function() { console.log('Clicked!'); };
element.addEventListener('click', handler);
element.removeEventListener('click', handler);

Event Listener Options

element.addEventListener('click', handleClick, {
  capture: true,        // Use capturing phase
  once: true,          // Remove listener after first execution
  passive: true        // Improve scroll performance
});

Common Event Patterns

Debouncing

Limit how often a function can be called:

function debounce(func, wait) {
  let timeout;
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
}

window.addEventListener('resize', debounce(function() {
  console.log('Window resized');
}, 250));

Throttling

Ensure function is called at most once per time interval:

function throttle(func, limit) {
  let inThrottle;
  return function() {
    const args = arguments;
    const context = this;
    if (!inThrottle) {
      func.apply(context, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

window.addEventListener('scroll', throttle(function() {
  console.log('Scrolled');
}, 100));

Event Emitter Pattern

class EventEmitter {
  constructor() {
    this.events = {};
  }

  on(event, listener) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(listener);
  }

  off(event, listener) {
    if (!this.events[event]) return;
    this.events[event] = this.events[event].filter(l => l !== listener);
  }

  emit(event, ...args) {
    if (!this.events[event]) return;
    this.events[event].forEach(listener => listener(...args));
  }
}

// Usage
let emitter = new EventEmitter();

function onUserLogin(user) {
  console.log('User logged in:', user.name);
}

emitter.on('login', onUserLogin);
emitter.emit('login', { name: 'John', id: 123 });

Touch Events

For mobile devices:

element.addEventListener('touchstart', function(event) {
  console.log('Touch started');
  let touch = event.touches[0];
  console.log('Touch X:', touch.clientX);
  console.log('Touch Y:', touch.clientY);
});

element.addEventListener('touchmove', function(event) {
  console.log('Touch moved');
  event.preventDefault(); // Prevent scrolling
});

element.addEventListener('touchend', function(event) {
  console.log('Touch ended');
});

element.addEventListener('touchcancel', function(event) {
  console.log('Touch cancelled');
});

Drag and Drop

let draggedElement;

document.addEventListener('dragstart', function(event) {
  draggedElement = event.target;
  event.dataTransfer.setData('text/html', event.target.outerHTML);
});

document.addEventListener('dragover', function(event) {
  event.preventDefault(); // Allow drop
});

document.addEventListener('drop', function(event) {
  event.preventDefault();
  let data = event.dataTransfer.getData('text/html');
  event.target.innerHTML = data;
});

Best Practices

Use Event Delegation

// Good - one listener on parent
document.querySelector('#list').addEventListener('click', function(event) {
  if (event.target.matches('li')) {
    handleListItemClick(event.target);
  }
});

// Avoid - listeners on each item
document.querySelectorAll('li').forEach(item => {
  item.addEventListener('click', handleListItemClick);
});

Clean Up Event Listeners

class Component {
  constructor() {
    this.handleClick = this.handleClick.bind(this);
    this.element.addEventListener('click', this.handleClick);
  }

  destroy() {
    this.element.removeEventListener('click', this.handleClick);
  }
}

Handle Browser Compatibility

function addEventListener(element, event, handler) {
  if (element.addEventListener) {
    element.addEventListener(event, handler, false);
  } else if (element.attachEvent) {
    element.attachEvent('on' + event, handler); // IE8 and below
  }
}

Use Passive Events for Performance

// For scroll events
element.addEventListener('scroll', handleScroll, { passive: true });

// For touch events
element.addEventListener('touchstart', handleTouch, { passive: true });

Avoid Inline Event Handlers

// Bad
<button onclick="handleClick()">Click me</button>

// Good
document.querySelector('button').addEventListener('click', handleClick);

Events are the primary way JavaScript interacts with user actions and browser state changes. Mastering event handling is essential for creating interactive web applications.

Loading