⚡ Phase 1 · Foundations 🟡 Intermediate MODULE 11

JavaScript Events

⏱️ 38 min read
📖 Theory + Code
🧩 5 Quiz Questions
🏗️ 1 Challenge
Your progress in Phase 185%
🎯 What you'll learn: Events are how users interact with web pages. You'll master addEventListener, the event object, event delegation, keyboard and mouse events, custom events, form validation, and preventing default browser behavior.

addEventListener Basics

The addEventListener method attaches a function to fire when a specific event occurs on an element. This is the modern, preferred way to handle events.

addEventListener Syntax & Common Events
JS
// Syntax: element.addEventListener(event, handler, options)
const btn = document.querySelector('#myBtn');

// Click events
btn.addEventListener('click', () => console.log('Clicked!'));
btn.addEventListener('dblclick', () => console.log('Double click!'));

// Mouse events
btn.addEventListener('mouseover', () => btn.style.opacity = '0.8');
btn.addEventListener('mouseout', () => btn.style.opacity = '1');
btn.addEventListener('mousemove', e => console.log(e.clientX, e.clientY));

// Keyboard events (on document or input)
document.addEventListener('keydown', e => console.log(e.key));
document.addEventListener('keyup', e => console.log(e.key));

// Form/Input events
const input = document.querySelector('input');
input.addEventListener('change', e => console.log(e.target.value));
input.addEventListener('input', e => console.log(e.target.value));  // fires every keystroke
input.addEventListener('focus', () => input.classList.add('focused'));
input.addEventListener('blur', () => input.classList.remove('focused'));

// Window events
window.addEventListener('resize', () => console.log(window.innerWidth));
window.addEventListener('scroll', () => console.log(window.scrollY), {passive:true});

The Event Object

Every event handler receives an event object (usually called e or event) containing useful information about what happened.

Event Object: target, preventDefault, stopPropagation
JS
// e.target — the element that was clicked
document.addEventListener('click', e => {
  console.log(e.target);          // element that was clicked
  console.log(e.target.tagName);   // 'BUTTON', 'DIV', etc.
  console.log(e.target.id);        // element's id
  console.log(e.target.dataset.id); // data-id attribute
});

// e.preventDefault() — stop default browser action
const link = document.querySelector('a');
link.addEventListener('click', e => {
  e.preventDefault();   // don't navigate
  console.log('Link clicked but not followed');
});

// Form submission — prevent page reload
const form = document.querySelector('form');
form.addEventListener('submit', e => {
  e.preventDefault();
  const data = new FormData(form);
  console.log(data.get('username'));
});

// e.stopPropagation() — stop event bubbling up the DOM
const inner = document.querySelector('.inner');
inner.addEventListener('click', e => {
  e.stopPropagation();  // parent click handler won't fire
});

Event Delegation

Instead of adding listeners to many elements, attach one listener to the parent and check e.target. This is more efficient and works for dynamically added elements.

Event Delegation Pattern
JS
// BAD — adding listener to each button (inefficient)
document.querySelectorAll('.delete-btn').forEach(btn => {
  btn.addEventListener('click', handleDelete);
});

// GOOD — one listener on the parent (event delegation)
const list = document.getElementById('taskList');

list.addEventListener('click', e => {
  // Check if the clicked element is a delete button
  if (e.target.matches('.delete-btn')) {
    e.target.closest('li').remove();
  }

  // Check for complete button
  if (e.target.matches('.complete-btn')) {
    e.target.closest('li').classList.toggle('done');
  }
});

// Now works for dynamically added items too!
function addTask(text) {
  list.insertAdjacentHTML('beforeend',
    `<li>${text}<button class="delete-btn">Delete</button></li>`
  );
}
💡
Why delegation matters
If you have 1000 list items and add a click listener to each, that's 1000 event listeners in memory. With delegation, it's just 1 listener on the parent — much more efficient. It also handles dynamically-added elements automatically.

Keyboard Events & Custom Events

Keyboard Shortcuts & Custom Events
JS
// Keyboard shortcuts
document.addEventListener('keydown', e => {
  // Ctrl+S — save
  if (e.ctrlKey && e.key === 's') {
    e.preventDefault();
    saveDocument();
  }

  // Escape — close modal
  if (e.key === 'Escape') closeModal();

  // Arrow keys — navigate
  if (e.key === 'ArrowUp') moveUp();
  if (e.key === 'ArrowDown') moveDown();

  // Check modifiers
  console.log(e.key, e.ctrlKey, e.shiftKey, e.altKey);
});

// Removing event listeners
function handleClick() { console.log('clicked'); }
btn.addEventListener('click', handleClick);
btn.removeEventListener('click', handleClick);  // must be same reference!

// Custom Events
const event = new CustomEvent('taskCompleted', {
  detail: { taskId: 42, taskName: 'Learn Events' },
  bubbles: true
});
document.dispatchEvent(event);

// Listen for custom event
document.addEventListener('taskCompleted', e => {
  console.log('Task done:', e.detail.taskName);
});

Real-World: Form Validation

Real-Time Form Validation
JS
const form = document.getElementById('signupForm');
const emailInput = document.getElementById('email');
const passwordInput = document.getElementById('password');

// Real-time validation as user types
emailInput.addEventListener('input', () => {
  const valid = emailInput.value.includes('@');
  emailInput.classList.toggle('invalid', !valid);
  document.getElementById('emailError').textContent =
    valid ? '' : 'Please enter a valid email';
});

passwordInput.addEventListener('input', () => {
  const val = passwordInput.value;
  const strength = val.length >= 12 ? 'Strong' :
                    val.length >= 8 ? 'Medium' : 'Weak';
  document.getElementById('strengthLabel').textContent = strength;
});

// Form submit
form.addEventListener('submit', e => {
  e.preventDefault();
  const data = {
    email: emailInput.value.trim(),
    password: passwordInput.value
  };
  if (!data.email || !data.password) return;
  submitForm(data);
});
🧩 Knowledge Check — Events
5 questions about JavaScript events.
1. What is the purpose of e.preventDefault()?
2. What does e.target refer to in an event handler?
3. What is event delegation?
4. Which keyboard event fires when a key is pressed down?
5. To remove an event listener you must pass the same ___.
🎮
Challenge — Interactive Quiz Game
Build a quiz with keyboard navigation · Intermediate
Build an Interactive Quiz with Keyboard Support

Create quiz-game.html:

1. Array of 5 quiz questions (each with options array and correctIndex)
2. Display one question at a time
3. Click option buttons OR press 1-4 on keyboard to answer
4. Press Enter/Space to go to next question
5. Show visual feedback: correct (green) / wrong (red)
6. Track score, show final result screen
7. Press 'R' to restart the quiz
💡 Show hints
  • Use a currentQuestion index variable to track state
  • Listen for keydown: if (e.key === '1') selectOption(0)
  • Use event delegation on the options container for click events
  • Disable options after answering: btn.disabled = true
Finished this lesson?
Mark it complete to track your progress.
🎉

Lesson 11 Complete!

Events mastered! One more lesson to go before projects. Next: Async JavaScript!

Module 11 of 13Phase 1 — JS Foundations