Events
What are Events?
Events are actions or occurrences that happen in the browser that JavaScript can respond to, such as clicks, key presses, mouse movements, and page loads.
Try Event Concepts
Code Editor
Console Output
Click "Run" to execute your code...
Example 1: Adding Event Listeners
// addEventListener - recommended approach
const button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log('Button clicked!');
});
// Multiple listeners on same event
button.addEventListener('click', function() {
console.log('Another click handler');
});
// Arrow function
button.addEventListener('click', () => {
console.log('Arrow function handler');
});
// Named function (can be removed later)
function handleClick(event) {
console.log('Clicked!', event);
}
button.addEventListener('click', handleClick);
// Remove event listener
button.removeEventListener('click', handleClick);
// Event listener with options
button.addEventListener('click', handler, {
once: true, // Remove after first trigger
capture: false, // Use capturing phase
passive: true // Won't call preventDefault()
});
// Old way (not recommended)
// button.onclick = function() { console.log('Old way'); };
// Problem: Only one handler per event
// Common events
element.addEventListener('click', handler); // Mouse click
element.addEventListener('dblclick', handler); // Double click
element.addEventListener('mouseenter', handler); // Mouse enters
element.addEventListener('mouseleave', handler); // Mouse leaves
element.addEventListener('mousemove', handler); // Mouse moves
element.addEventListener('mousedown', handler); // Mouse button down
element.addEventListener('mouseup', handler); // Mouse button up
// Keyboard events
input.addEventListener('keydown', handler); // Key pressed
input.addEventListener('keyup', handler); // Key released
input.addEventListener('keypress', handler); // Key pressed (deprecated)
// Form events
form.addEventListener('submit', handler);
input.addEventListener('change', handler);
input.addEventListener('input', handler);
input.addEventListener('focus', handler);
input.addEventListener('blur', handler);
// Window events
window.addEventListener('load', handler); // Page fully loaded
window.addEventListener('DOMContentLoaded', handler); // DOM ready
window.addEventListener('resize', handler); // Window resized
window.addEventListener('scroll', handler); // Page scrolledExample 2: Event Object
// Event object contains information about the event
button.addEventListener('click', function(event) {
console.log(event);
// Common event properties
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 events
console.log(event.clientX); // X coordinate (viewport)
console.log(event.clientY); // Y coordinate (viewport)
console.log(event.pageX); // X coordinate (page)
console.log(event.pageY); // Y coordinate (page)
console.log(event.button); // Which mouse button (0=left, 1=middle, 2=right)
// Keyboard modifiers
console.log(event.ctrlKey); // Ctrl key pressed?
console.log(event.shiftKey); // Shift key pressed?
console.log(event.altKey); // Alt key pressed?
console.log(event.metaKey); // Meta/Command key pressed?
});
// Keyboard events
input.addEventListener('keydown', function(event) {
console.log(event.key); // Key value ('a', 'Enter', 'ArrowUp')
console.log(event.code); // Physical key code ('KeyA', 'Enter')
console.log(event.keyCode); // Numeric code (deprecated)
// Check specific keys
if (event.key === 'Enter') {
console.log('Enter pressed');
}
if (event.ctrlKey && event.key === 's') {
event.preventDefault(); // Prevent browser save dialog
console.log('Ctrl+S pressed');
}
});
// Form events
form.addEventListener('submit', function(event) {
event.preventDefault(); // Prevent form submission
// Get form data
const formData = new FormData(event.target);
console.log(Object.fromEntries(formData));
});
// Input events
input.addEventListener('input', function(event) {
console.log('Current value:', event.target.value);
});
// Touch events (mobile)
element.addEventListener('touchstart', function(event) {
const touch = event.touches[0];
console.log(touch.clientX, touch.clientY);
});Example 3: Event Bubbling and Capturing
// Event phases:
// 1. Capturing phase (top to bottom)
// 2. Target phase (at the element)
// 3. Bubbling phase (bottom to top)
// HTML structure:
// <div id="outer">
// <div id="middle">
// <button id="inner">Click Me</button>
// </div>
// </div>
const outer = document.getElementById('outer');
const middle = document.getElementById('middle');
const inner = document.getElementById('inner');
// Bubbling phase (default)
outer.addEventListener('click', () => console.log('Outer clicked'));
middle.addEventListener('click', () => console.log('Middle clicked'));
inner.addEventListener('click', () => console.log('Inner clicked'));
// Click inner button outputs:
// Inner clicked
// Middle clicked
// Outer clicked
// Capturing phase (useCapture = true)
outer.addEventListener('click', () => console.log('Outer capturing'), true);
middle.addEventListener('click', () => console.log('Middle capturing'), true);
inner.addEventListener('click', () => console.log('Inner capturing'), true);
// Click inner button outputs:
// Outer capturing
// Middle capturing
// Inner capturing
// Inner clicked (target)
// Middle clicked
// Outer clicked
// Stop propagation
inner.addEventListener('click', (event) => {
console.log('Inner clicked');
event.stopPropagation(); // Stops bubbling
});
// Now only outputs: Inner clicked
// Stop immediate propagation
inner.addEventListener('click', (event) => {
console.log('First handler');
event.stopImmediatePropagation(); // Stops other handlers on same element
});
inner.addEventListener('click', () => {
console.log('Second handler'); // Won't execute
});
// Prevent default behavior
link.addEventListener('click', (event) => {
event.preventDefault(); // Prevents navigation
console.log('Link clicked but not navigated');
});
// Check if default was prevented
element.addEventListener('click', (event) => {
if (event.defaultPrevented) {
console.log('Default action was prevented');
}
});Example 4: Event Delegation
// Problem: Adding listeners to many elements
// const items = document.querySelectorAll('.item');
// items.forEach(item => {
// item.addEventListener('click', handleClick); // Many listeners!
// });
// Solution: Event delegation (use bubbling)
const list = document.getElementById('list');
list.addEventListener('click', function(event) {
// Check if clicked element matches
if (event.target.matches('.item')) {
console.log('Item clicked:', event.target.textContent);
}
});
// Benefits:
// 1. One listener instead of many
// 2. Works for dynamically added elements
// 3. Better memory usage
// 4. Better performance
// Practical example: Todo list
const todoList = document.getElementById('todo-list');
todoList.addEventListener('click', function(event) {
const target = event.target;
// Delete button clicked
if (target.matches('.delete-btn')) {
const todo = target.closest('.todo-item');
todo.remove();
}
// Checkbox clicked
if (target.matches('.todo-checkbox')) {
const todo = target.closest('.todo-item');
todo.classList.toggle('completed');
}
// Edit button clicked
if (target.matches('.edit-btn')) {
const todo = target.closest('.todo-item');
const text = todo.querySelector('.todo-text');
text.contentEditable = true;
text.focus();
}
});
// Dynamic content example
const container = document.getElementById('container');
container.addEventListener('click', function(event) {
if (event.target.matches('button')) {
console.log('Button clicked:', event.target.textContent);
}
});
// Add new buttons dynamically
function addButton(text) {
const btn = document.createElement('button');
btn.textContent = text;
container.appendChild(btn);
// Button automatically has click handler!
}
addButton('Button 1');
addButton('Button 2');Example 5: Custom Events
// Creating custom events
const myEvent = new Event('myCustomEvent');
element.addEventListener('myCustomEvent', function(event) {
console.log('Custom event triggered!');
});
// Dispatch the event
element.dispatchEvent(myEvent);
// Custom event with data
const detailEvent = new CustomEvent('userLogin', {
detail: {
username: 'john',
timestamp: Date.now()
},
bubbles: true,
cancelable: true
});
document.addEventListener('userLogin', function(event) {
console.log('User logged in:', event.detail.username);
console.log('Time:', event.detail.timestamp);
});
document.dispatchEvent(detailEvent);
// Practical example: Component communication
class Component {
constructor(element) {
this.element = element;
}
emit(eventName, data) {
const event = new CustomEvent(eventName, {
detail: data,
bubbles: true
});
this.element.dispatchEvent(event);
}
on(eventName, handler) {
this.element.addEventListener(eventName, handler);
}
}
// Usage
const component = new Component(document.getElementById('myComponent'));
component.on('dataChanged', (event) => {
console.log('Data changed:', event.detail);
});
component.emit('dataChanged', { value: 42 });
// Event-based architecture
const eventBus = {
events: {},
on(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
},
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach(callback => callback(data));
}
},
off(event, callback) {
if (this.events[event]) {
this.events[event] = this.events[event].filter(cb => cb !== callback);
}
}
};
// Usage
eventBus.on('userLoggedIn', (user) => {
console.log('User logged in:', user);
});
eventBus.emit('userLoggedIn', { name: 'John', id: 1 });Key Points
- Use addEventListener() to attach event handlers
- Event object contains information about the event
- Events bubble up from target to document by default
- Use event delegation for better performance with many elements
- preventDefault() stops default behavior, stopPropagation() stops bubbling
- Custom events enable component communication
- Remove event listeners to prevent memory leaks