Home Module 11 Event Types

1. Introduction

The browser fires dozens of event types. This lesson covers the ones you will use in almost every project, grouped by category, with the key properties of each event object.

2. Theory

2.1 Mouse events

EventWhen it fires
clickMouse button pressed and released on element
dblclickDouble-click
mousedownMouse button pressed (before release)
mouseupMouse button released
mouseenterCursor enters element (does not bubble)
mouseleaveCursor leaves element (does not bubble)
mouseoverCursor enters element or child (bubbles)
mouseoutCursor leaves element or child (bubbles)
mousemoveCursor moves over element
contextmenuRight-click
el.addEventListener('click', e => {
  console.log(e.clientX, e.clientY); // position in viewport
  console.log(e.pageX, e.pageY);     // position in document (includes scroll)
  console.log(e.button);             // 0=left, 1=middle, 2=right
  console.log(e.ctrlKey, e.shiftKey, e.altKey); // modifier keys held
});

2.2 Keyboard events

EventWhen
keydownKey pressed (fires repeatedly if held)
keyupKey released
keypressDeprecated — use keydown
document.addEventListener('keydown', e => {
  console.log(e.key);     // 'Enter', 'a', 'ArrowLeft', ' ' (space)
  console.log(e.code);    // 'KeyA', 'Space', 'ArrowLeft' (physical key)
  console.log(e.ctrlKey); // true if Ctrl held

  // Common patterns
  if (e.key === 'Escape')  closeModal();
  if (e.key === 'Enter')   submitForm();
  if (e.ctrlKey && e.key === 's') {
    e.preventDefault();
    saveDocument();
  }
});

e.key gives the character/key name; e.code gives the physical key position (useful for games where you care about key location, not character).

2.3 Form events

EventWhen / where
submitForm submitted (button click or Enter)
inputValue changes on input/textarea/select (fires every change)
changeValue committed — fires on blur for text inputs, immediately for checkboxes/select
focusElement receives focus (does not bubble)
blurElement loses focus (does not bubble)
focusinLike focus but bubbles
focusoutLike blur but bubbles
resetForm reset button clicked
// Real-time character count
textarea.addEventListener('input', e => {
  charCount.textContent = e.target.value.length;
});

// Validate when user leaves field
emailInput.addEventListener('blur', e => {
  if (!e.target.value.includes('@')) {
    e.target.classList.add('error');
  }
});

// Handle select change
countrySelect.addEventListener('change', e => {
  loadRegions(e.target.value);
});

2.4 Window / document events

EventWhen
DOMContentLoadedHTML parsed, DOM built (on document)
loadPage fully loaded including images (on window)
beforeunloadUser about to leave (can show confirm dialog)
resizeWindow resized
scrollPage or element scrolled
online/offlineNetwork connectivity changed
visibilitychangeTab hidden or shown
// Warn before leaving with unsaved changes
let isDirty = false;
form.addEventListener('input', () => isDirty = true);
window.addEventListener('beforeunload', e => {
  if (isDirty) {
    e.preventDefault();
    e.returnValue = ''; // required for some browsers
  }
});

// Responsive JS — react to window resize
window.addEventListener('resize', throttle(() => {
  console.log('Width:', window.innerWidth);
}, 200));

// Pause expensive work when tab is hidden
document.addEventListener('visibilitychange', () => {
  if (document.hidden) pauseAnimation();
  else resumeAnimation();
});

2.5 Clipboard events

document.addEventListener('copy', e => {
  const selected = window.getSelection().toString();
  console.log('Copied:', selected);
});

document.addEventListener('paste', e => {
  const text = e.clipboardData.getData('text/plain');
  console.log('Pasted:', text);
  e.preventDefault(); // prevent paste into the page
});

2.6 Drag and drop events

// On the draggable element
el.addEventListener('dragstart', e => {
  e.dataTransfer.setData('text/plain', el.id);
});

// On the drop target
dropZone.addEventListener('dragover', e => {
  e.preventDefault(); // required to allow drop
  dropZone.classList.add('drag-over');
});
dropZone.addEventListener('dragleave', () => {
  dropZone.classList.remove('drag-over');
});
dropZone.addEventListener('drop', e => {
  e.preventDefault();
  const id = e.dataTransfer.getData('text/plain');
  dropZone.appendChild(document.getElementById(id));
});

2.7 Custom events

// Create and dispatch a custom event
const event = new CustomEvent('cart:updated', {
  detail: { count: 5, total: 49.99 },
  bubbles: true,
});
document.dispatchEvent(event);

// Listen for it anywhere
document.addEventListener('cart:updated', e => {
  cartBadge.textContent = e.detail.count;
});

3. Real World Example

// Keyboard-accessible modal
const modal    = document.querySelector('#modal');
const openBtn  = document.querySelector('#open-modal');
const closeBtn = modal.querySelector('.close');

function openModal() {
  modal.classList.add('visible');
  modal.setAttribute('aria-hidden', 'false');
  closeBtn.focus(); // move focus into modal
}

function closeModal() {
  modal.classList.remove('visible');
  modal.setAttribute('aria-hidden', 'true');
  openBtn.focus(); // return focus to trigger
}

openBtn.addEventListener('click', openModal);
closeBtn.addEventListener('click', closeModal);

// Close on Escape key
document.addEventListener('keydown', e => {
  if (e.key === 'Escape' && modal.classList.contains('visible')) {
    closeModal();
  }
});

// Close on backdrop click
modal.addEventListener('click', e => {
  if (e.target === modal) closeModal(); // only if clicking backdrop, not content
});

4. Code Example

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Event Types Demo</title></head>
<body>
  <div id="mouse-box" style="width:200px;height:200px;background:#e0e0e0;display:flex;align-items:center;justify-content:center">
    Hover / Click me
  </div>
  <input id="key-input" placeholder="Press keys here" style="margin:1rem 0;display:block">
  <input id="search" placeholder="Debounced search">
  <p id="output"></p>

  <script>
    const out = document.querySelector('#output');
    const box = document.querySelector('#mouse-box');

    // Mouse events
    box.addEventListener('mouseenter', () => box.style.background = '#93c5fd');
    box.addEventListener('mouseleave', () => box.style.background = '#e0e0e0');
    box.addEventListener('click', e => out.textContent = `Clicked at (${e.clientX}, ${e.clientY})`);
    box.addEventListener('dblclick', () => out.textContent = 'Double-clicked!');

    // Keyboard events
    document.querySelector('#key-input').addEventListener('keydown', e => {
      out.textContent = `Key: "${e.key}" | Code: ${e.code} | Ctrl:${e.ctrlKey}`;
    });

    // Debounced input
    let timer;
    document.querySelector('#search').addEventListener('input', e => {
      clearTimeout(timer);
      timer = setTimeout(() => {
        out.textContent = `Searching for: "${e.target.value}"`;
      }, 400);
    });

    // Window events
    window.addEventListener('resize', () => {
      document.title = `${window.innerWidth}×${window.innerHeight}`;
    });
  </script>
</body>
</html>

5. Code Breakdown

mouseenter vs click

mouseenter/mouseleave fire exactly once when the cursor crosses the element boundary — perfect for hover styles. click fires when the full press-and-release cycle completes on the same element.

e.key vs e.code

e.key gives the character ("a", "Enter", "ArrowLeft"). e.code gives the physical key ("KeyA", "Enter", "ArrowLeft"). For text input handling use e.key. For game controls where position matters (WASD) use e.code.

Inline debounce

A manual debounce using closured timer. Each keystroke clears the previous timeout and sets a new one. Only the final keystroke's timeout completes (400ms silence).

6. Common Mistakes

Mistake 1 — Using keypress (deprecated)

// Bad — keypress is deprecated
input.addEventListener('keypress', handler);
// Good — use keydown or keyup
input.addEventListener('keydown', handler);

Mistake 2 — Confusing input and change events

// input fires on every keystroke — best for real-time feedback
input.addEventListener('input', updatePreview);
// change fires when value is committed (on blur for text fields)
input.addEventListener('change', saveToStorage); // fires after user leaves field

Mistake 3 — mouseenter vs mouseover confusion

// mouseover BUBBLES — fires when cursor enters a child element too
// mouseenter does NOT bubble — fires only when cursor enters THIS element
// For hover effects, use mouseenter/mouseleave

7. Best Practices

  1. Use keydown, not the deprecated keypress.
  2. Use mouseenter/mouseleave for hover effects (no bubbling surprises).
  3. Use input for real-time feedback, change for committed value changes.
  4. Debounce resize and scroll handlers — they fire many times per second.
  5. Use visibilitychange to pause expensive work when the tab is hidden.
  6. Use CustomEvent for component communication instead of tight coupling.

8. Practice Exercise

  1. Build a "Keyboard Visualiser" — a div that shows each key pressed, highlights modifier keys, and plays a visual flash for Shift/Ctrl/Alt.
  2. Build a form with: email (validate on blur), message textarea (character counter with max 200, input event), and a submit that only works if both fields are valid.
  3. Create a drag-and-drop list where items can be rearranged by dragging.

9. Assignment

Build a "Markdown-style Text Editor."

  1. A textarea for writing, a preview div on the right.
  2. On input event (debounced 300ms), convert simple markdown to HTML and show in the preview (replace **bold** with <strong>, *italic* with <em>, and newlines with <br>).
  3. Implement keyboard shortcuts: Ctrl+B wraps selected text in **, Ctrl+I wraps in *.
  4. Warn with beforeunload if there is unsaved content.
  5. Add a character/word count that updates on input.

Deliverable: One HTML file.

10. Interview Questions

  1. What is the difference between the input and change events?
    input fires on every value change (every keystroke). change fires when the value is committed — on blur for text inputs, immediately for checkboxes, select, and radio buttons.
  2. What is the difference between e.key and e.code?
    e.key gives the logical key value ('a', 'Enter'). e.code gives the physical key identifier ('KeyA', 'Enter'). Use e.key for character/shortcut detection; use e.code for layout-independent key detection (e.g., game controls).
  3. What is the difference between mouseenter and mouseover?
    mouseenter fires only when the cursor enters the element itself and does not bubble. mouseover fires when the cursor enters the element or any of its children and bubbles. Use mouseenter/mouseleave for hover effects to avoid false triggers from child elements.
  4. What is a CustomEvent?
    A user-defined event created with new CustomEvent('name', { detail: data }). It can be dispatched on any DOM element with dispatchEvent() and listened for with addEventListener. Used for decoupled component communication.

11. Additional Resources

  • MDN — Event reference — complete list of all event types
  • MDN — KeyboardEvent
  • MDN — MouseEvent
  • MDN — Drag and Drop API
  • javascript.info — Mouse events
  • javascript.info — Keyboard events