Events
User interaction handling
JavaScript Events
Events are actions that happen in the browser (clicks, key presses, page loads, etc.). JavaScript can "listen" for these events and respond to them.
Adding Event Listeners
const button = document.querySelector('#my-button');
// Modern way (preferred)
button.addEventListener('click', () => {
console.log('Button clicked!');
});
// Can add multiple listeners to same event
button.addEventListener('click', () => {
console.log('Another listener');
});
// Old way (avoid - can only have one handler)
button.onclick = () => {
console.log('Old way');
};
Common Event Types
Mouse Events
element.addEventListener('click', (e) => {});
element.addEventListener('dblclick', (e) => {}); // Double click
element.addEventListener('mouseenter', (e) => {}); // Mouse enters
element.addEventListener('mouseleave', (e) => {}); // Mouse leaves
element.addEventListener('mousemove', (e) => {}); // Mouse moves
element.addEventListener('mousedown', (e) => {}); // Mouse button down
element.addEventListener('mouseup', (e) => {}); // Mouse button up
Keyboard Events
element.addEventListener('keydown', (e) => {
console.log('Key pressed:', e.key);
if (e.key === 'Enter') {
console.log('Enter key pressed');
}
});
element.addEventListener('keyup', (e) => {});
element.addEventListener('keypress', (e) => {}); // Deprecated
Form Events
const form = document.querySelector('form');
const input = document.querySelector('input');
form.addEventListener('submit', (e) => {
e.preventDefault(); // Prevent page reload
console.log('Form submitted');
});
input.addEventListener('input', (e) => {
console.log('Value:', e.target.value); // Fires on every change
});
input.addEventListener('change', (e) => {
console.log('Changed:', e.target.value); // Fires when input loses focus
});
input.addEventListener('focus', (e) => {});
input.addEventListener('blur', (e) => {}); // Loses focus
Window/Document Events
// Page load
window.addEventListener('load', () => {
console.log('Page fully loaded');
});
// DOM ready
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM ready');
});
// Scroll
window.addEventListener('scroll', () => {
console.log('Scrolled to:', window.scrollY);
});
// Resize
window.addEventListener('resize', () => {
console.log('Window size:', window.innerWidth, window.innerHeight);
});
The Event Object
Event listeners receive an event object with useful information:
button.addEventListener('click', (event) => {
console.log(event.type); // "click"
console.log(event.target); // Element that triggered event
console.log(event.currentTarget); // Element with listener attached
console.log(event.timeStamp); // When event occurred
// Mouse position
console.log(event.clientX, event.clientY); // Relative to viewport
console.log(event.pageX, event.pageY); // Relative to page
// Keyboard info
console.log(event.key); // "a", "Enter", "Escape", etc.
console.log(event.code); // "KeyA", "Enter", etc.
console.log(event.ctrlKey); // Was Ctrl pressed?
console.log(event.shiftKey); // Was Shift pressed?
});
Event Propagation
Events bubble up from child to parent elements:
// All three will fire when button is clicked!
document.querySelector('#btn').addEventListener('click', () => {
console.log('Button clicked');
});
document.querySelector('#inner').addEventListener('click', () => {
console.log('Inner div clicked');
});
document.querySelector('#outer').addEventListener('click', () => {
console.log('Outer div clicked');
});
// Output when button clicked:
// "Button clicked"
// "Inner div clicked"
// "Outer div clicked"
Stopping Propagation
button.addEventListener('click', (e) => {
e.stopPropagation(); // Stop event from bubbling up
console.log('Only this runs');
});
// Prevent default browser behavior
link.addEventListener('click', (e) => {
e.preventDefault(); // Don't follow the link
console.log('Link click prevented');
});
form.addEventListener('submit', (e) => {
e.preventDefault(); // Don't reload page
// Handle form with JavaScript instead
});
Event Delegation
Instead of adding listeners to many elements, add one to a parent:
// BAD: Adding listener to each item
const items = document.querySelectorAll('.item');
items.forEach(item => {
item.addEventListener('click', () => {
console.log('Item clicked');
});
});
// GOOD: Event delegation
const list = document.querySelector('#item-list');
list.addEventListener('click', (e) => {
// Check if clicked element is an item
if (e.target.classList.contains('item')) {
console.log('Item clicked:', e.target.textContent);
}
});
// Works for dynamically added items too!
const newItem = document.createElement('div');
newItem.className = 'item';
newItem.textContent = 'New Item';
list.appendChild(newItem); // Click handler automatically works!
Removing Event Listeners
// Named function (required for removal)
function handleClick() {
console.log('Clicked');
}
button.addEventListener('click', handleClick);
// Remove later
button.removeEventListener('click', handleClick);
// Anonymous functions can't be removed!
button.addEventListener('click', () => {}); // Can't remove this
Practical Examples
// Example 1: Dropdown menu
const dropdown = document.querySelector('.dropdown');
const menu = document.querySelector('.dropdown-menu');
dropdown.addEventListener('mouseenter', () => {
menu.classList.add('show');
});
dropdown.addEventListener('mouseleave', () => {
menu.classList.remove('show');
});
// Example 2: Keyboard shortcuts
document.addEventListener('keydown', (e) => {
// Ctrl+S to save
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
console.log('Save triggered');
}
// Escape to close modal
if (e.key === 'Escape') {
modal.classList.add('hidden');
}
});
// Example 3: Infinite scroll
window.addEventListener('scroll', () => {
const scrolled = window.scrollY + window.innerHeight;
const total = document.documentElement.scrollHeight;
if (scrolled >= total - 100) {
console.log('Near bottom - load more content');
}
});
⚠️ Common Pitfalls
- • Forgetting to
preventDefault()on forms/links - • Adding listeners in loops without delegation
- • Not removing listeners when no longer needed (memory leaks)
- • Using anonymous functions when you need to remove listener later
✓ Best Practices
- • Use event delegation for lists of similar elements
- • Always
preventDefault()on form submissions if handling with JS - • Debounce expensive handlers (scroll, resize, input)
- • Use named functions if you need to remove listeners
- • Check
event.targetin delegated handlers