Docs
README
10.3 Events and Event Handling
π Learning Objectives
By the end of this section, you will:
- β’Understand the event system in the browser
- β’Add and remove event listeners
- β’Work with the event object and its properties
- β’Understand event propagation (bubbling and capturing)
- β’Prevent default behaviors and stop propagation
- β’Handle common event types
π Table of Contents
- β’Understanding Events
- β’Adding Event Listeners
- β’The Event Object
- β’Event Propagation
- β’Controlling Event Behavior
- β’Common Event Types
Understanding Events
What are Events?
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser Event System β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β User Action Browser Your Code β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β β Click βββββββββΆβ Event βββββββββββββββΆβ Handler β β
β β Type β β Created β β Function β β
β β Scroll β ββββββββββββ ββββββββββββ β
β β etc. β β
β ββββββββββββ β
β β
β Events can be triggered by: β
β β’ User interactions (click, type, scroll) β
β β’ Browser actions (load, resize, error) β
β β’ API/Code (custom events) β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Event Categories
| Category | Events | Description |
|---|---|---|
| Mouse | click, dblclick, mousedown, mouseup, mousemove, mouseenter, mouseleave | User mouse interactions |
| Keyboard | keydown, keyup, keypress (deprecated) | Keyboard input |
| Form | submit, change, input, focus, blur | Form interactions |
| Document | DOMContentLoaded, load, beforeunload | Page lifecycle |
| Window | resize, scroll, hashchange | Browser window |
| Touch | touchstart, touchmove, touchend | Mobile touch |
Adding Event Listeners
addEventListener Method
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β addEventListener Syntax β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β element.addEventListener(type, handler, options) β
β β β β β
β β β ββ Optional config β
β β ββ Function to call β
β ββ Event name (string) β
β β
β Options object: β
β { β
β capture: false, // Use capture phase β
β once: false, // Remove after first call β
β passive: false // Never call preventDefault β
β } β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Basic Event Listener
const button = document.getElementById('myButton');
// Method 1: Named function
function handleClick(event) {
console.log('Button clicked!', event);
}
button.addEventListener('click', handleClick);
// Method 2: Anonymous function
button.addEventListener('click', function (event) {
console.log('Clicked!');
});
// Method 3: Arrow function
button.addEventListener('click', (event) => {
console.log('Arrow function handler');
});
Removing Event Listeners
const button = document.getElementById('myButton');
function handleClick() {
console.log('Clicked!');
}
// Add listener
button.addEventListener('click', handleClick);
// Remove listener (must use same function reference)
button.removeEventListener('click', handleClick);
// β This won't work - different function references
button.addEventListener('click', function () {
console.log('A');
});
button.removeEventListener('click', function () {
console.log('A');
});
Using Options
const button = document.getElementById('myButton');
// Run only once
button.addEventListener(
'click',
() => {
console.log('This runs only once!');
},
{ once: true }
);
// Passive listener (for performance in scroll/touch)
window.addEventListener('scroll', handleScroll, { passive: true });
// Capture phase listener
element.addEventListener('click', handler, { capture: true });
// OR shorthand:
element.addEventListener('click', handler, true);
Old Way vs Modern Way
// β Old ways (avoid)
button.onclick = function() { }; // Only one handler possible
<button onclick="handler()"> // Inline (avoid)
// β Modern way (recommended)
button.addEventListener("click", handler); // Multiple handlers OK
The Event Object
Event Object Properties
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Event Object Properties β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β General Properties: β
β βββ type β Event type ("click", "keydown", etc.) β
β βββ target β Element that triggered the event β
β βββ currentTarget β Element handler is attached to β
β βββ timeStamp β When event was created β
β βββ isTrusted β User action (true) or script (false) β
β β
β Mouse Event Properties: β
β βββ clientX/Y β Coordinates relative to viewport β
β βββ pageX/Y β Coordinates relative to document β
β βββ offsetX/Y β Coordinates relative to target element β
β βββ screenX/Y β Coordinates relative to screen β
β βββ button β Which mouse button (0, 1, 2) β
β βββ buttons β Pressed buttons (bitfield) β
β β
β Keyboard Event Properties: β
β βββ key β Key value ("a", "Enter", "ArrowUp") β
β βββ code β Physical key ("KeyA", "Enter") β
β βββ altKey β Alt key pressed? β
β βββ ctrlKey β Ctrl key pressed? β
β βββ shiftKey β Shift key pressed? β
β βββ metaKey β Meta (Cmd/Win) key pressed? β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Using Event Properties
// Mouse event example
element.addEventListener('click', (event) => {
console.log('Event type:', event.type);
console.log('Target element:', event.target);
console.log('Current target:', event.currentTarget);
console.log('Viewport position:', event.clientX, event.clientY);
console.log('Page position:', event.pageX, event.pageY);
});
// Keyboard event example
document.addEventListener('keydown', (event) => {
console.log('Key pressed:', event.key);
console.log('Key code:', event.code);
console.log('Shift held:', event.shiftKey);
console.log('Ctrl held:', event.ctrlKey);
// Check for keyboard shortcuts
if (event.ctrlKey && event.key === 's') {
event.preventDefault();
console.log('Ctrl+S pressed');
}
});
target vs currentTarget
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β target vs currentTarget β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β HTML: β
β <div id="parent"> β
β <button id="child">Click</button> β
β </div> β
β β
β JavaScript: β
β parent.addEventListener("click", (e) => { β
β console.log(e.target); // <button> (clicked element) β
β console.log(e.currentTarget); // <div> (listener element) β
β }); β
β β
β When clicking the button: β
β β’ target = button (what you clicked) β
β β’ currentTarget = div (where listener is attached) β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Event Propagation
Phases of Event Propagation
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Event Propagation Phases β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββββββββββββββββββββββββββββββββ β
β β Document β β
β β ββββββββββββββββββββββββββββββββββββ β β
β β β <html> β β β
β β β ββββββββββββββββββββββββββββββ β β β
β β β β <body> β β β β
β β β β ββββββββββββββββββββββββ β β β β
β β β β β <div> β β β β β
β β β β β ββββββββββββββββ β β β β β
β β β β β β <button> β β β β β β
β 1 β β β β β CLICK! β β β β β 3 β
β Capture β β β β ββββββββββββββββ β β β β Bubble β
β Down β β β ββββββββββββββββββββββββ β β β Up β
β β β β ββββββββββββββββββββββββββββββ β β β β
β β ββββββββββββββββββββββββββββββββββββ β β
β ββββββββββββββββββββββββββββββββββββββββββ β
β β
β Phase 1: CAPTURING - Event travels DOWN from window to target β
β Phase 2: TARGET - Event reaches the target element β
β Phase 3: BUBBLING - Event travels UP from target to window β
β β
β By default, handlers run in BUBBLING phase β
β Use { capture: true } to run in CAPTURING phase β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Bubbling Example
// HTML: <div><p><span>Click me</span></p></div>
document.querySelector('span').addEventListener('click', () => {
console.log('1. span clicked'); // Runs first
});
document.querySelector('p').addEventListener('click', () => {
console.log('2. p clicked'); // Runs second
});
document.querySelector('div').addEventListener('click', () => {
console.log('3. div clicked'); // Runs third
});
// Clicking the span outputs:
// 1. span clicked
// 2. p clicked
// 3. div clicked
Capturing Example
// Add listeners in capture phase
document.querySelector('div').addEventListener(
'click',
() => {
console.log('1. div (capture)');
},
true
); // capture: true
document.querySelector('p').addEventListener(
'click',
() => {
console.log('2. p (capture)');
},
true
);
document.querySelector('span').addEventListener('click', () => {
console.log('3. span (target)');
});
// Clicking the span outputs:
// 1. div (capture) - capture phase
// 2. p (capture) - capture phase
// 3. span (target) - target phase
Controlling Event Behavior
preventDefault()
// Prevent default link behavior
link.addEventListener('click', (event) => {
event.preventDefault();
console.log('Link click prevented');
});
// Prevent form submission
form.addEventListener('submit', (event) => {
event.preventDefault();
console.log('Form submission prevented');
// Handle form data with JavaScript instead
});
// Prevent right-click context menu
element.addEventListener('contextmenu', (event) => {
event.preventDefault();
showCustomMenu(event.clientX, event.clientY);
});
stopPropagation()
// Prevent event from bubbling up
innerElement.addEventListener('click', (event) => {
event.stopPropagation();
console.log("Click handled here, won't bubble");
});
// Outer handler won't run if inner calls stopPropagation
outerElement.addEventListener('click', () => {
console.log("This won't run if inner element is clicked");
});
stopImmediatePropagation()
// Stop all other handlers, even on same element
element.addEventListener('click', (event) => {
console.log('Handler 1');
event.stopImmediatePropagation();
});
element.addEventListener('click', () => {
console.log("Handler 2 - won't run!");
});
Comparison Table
| Method | Stops Bubbling | Stops Other Handlers on Same Element | Stops Default Behavior |
|---|---|---|---|
preventDefault() | β | β | β |
stopPropagation() | β | β | β |
stopImmediatePropagation() | β | β | β |
Common Event Types
Mouse Events
const element = document.getElementById('target');
// Click events
element.addEventListener('click', (e) => console.log('Single click'));
element.addEventListener('dblclick', (e) => console.log('Double click'));
// Mouse button events
element.addEventListener('mousedown', (e) => console.log('Mouse button down'));
element.addEventListener('mouseup', (e) => console.log('Mouse button up'));
// Mouse movement
element.addEventListener('mousemove', (e) => {
console.log(`Mouse at: ${e.clientX}, ${e.clientY}`);
});
// Enter/leave (don't bubble)
element.addEventListener('mouseenter', (e) => console.log('Mouse entered'));
element.addEventListener('mouseleave', (e) => console.log('Mouse left'));
// Over/out (bubble)
element.addEventListener('mouseover', (e) => console.log('Mouse over'));
element.addEventListener('mouseout', (e) => console.log('Mouse out'));
Keyboard Events
// Modern keyboard handling
document.addEventListener('keydown', (event) => {
// Use event.key for character
console.log('Key:', event.key);
// Use event.code for physical key
console.log('Code:', event.code);
// Common checks
if (event.key === 'Enter') {
console.log('Enter pressed');
}
if (event.key === 'Escape') {
console.log('Escape pressed');
}
// Arrow keys
if (event.key.startsWith('Arrow')) {
console.log('Arrow key:', event.key);
}
// Keyboard shortcuts
if (event.ctrlKey && event.key === 's') {
event.preventDefault();
saveDocument();
}
});
document.addEventListener('keyup', (event) => {
console.log('Key released:', event.key);
});
Form Events
const form = document.getElementById('myForm');
const input = document.getElementById('email');
// Form submission
form.addEventListener('submit', (event) => {
event.preventDefault();
const formData = new FormData(form);
console.log('Form submitted:', Object.fromEntries(formData));
});
// Input events
input.addEventListener('input', (event) => {
console.log('Value changed:', event.target.value);
});
input.addEventListener('change', (event) => {
console.log('Input changed (on blur):', event.target.value);
});
// Focus events
input.addEventListener('focus', () => console.log('Input focused'));
input.addEventListener('blur', () => console.log('Input blurred'));
Window/Document Events
// Page load
window.addEventListener('DOMContentLoaded', () => {
console.log('DOM ready (HTML parsed)');
});
window.addEventListener('load', () => {
console.log('Page fully loaded (including images)');
});
// Before leaving page
window.addEventListener('beforeunload', (event) => {
event.preventDefault();
event.returnValue = ''; // Show confirmation dialog
});
// Window resize
window.addEventListener('resize', () => {
console.log('Window size:', window.innerWidth, window.innerHeight);
});
// Scroll
window.addEventListener('scroll', () => {
console.log('Scroll position:', window.scrollY);
});
Event Flow Summary
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Event Handling Summary β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Adding Listeners: β
β β’ element.addEventListener(type, handler) // Standard β
β β’ element.addEventListener(type, handler, options) // With optionsβ
β β
β Removing Listeners: β
β β’ element.removeEventListener(type, handler) // Same reference! β
β β
β Event Object: β
β β’ e.target - Element that triggered event β
β β’ e.currentTarget - Element with the listener β
β β’ e.type - Event type string β
β β’ e.preventDefault() - Stop default action β
β β’ e.stopPropagation() - Stop bubbling β
β β
β Propagation: β
β β’ Capture β Target β Bubble β
β β’ Default handlers run in bubble phase β
β β’ Use { capture: true } for capture phase β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π‘ Key Takeaways
- β’Use addEventListener: Always prefer addEventListener over inline handlers
- β’Save Function References: To remove listeners, you need the same function reference
- β’Use Event Object: Access event details through the event parameter
- β’Understand Propagation: Events bubble up by default; use stopPropagation when needed
- β’Prevent Defaults Wisely: Only preventDefault when you're handling the action yourself
π Next Steps
Continue to 10.4 Event Delegation to learn how to efficiently handle events for multiple elements.