JavaScript Event Handling Cheat Sheet

JavaScript Event Handling Cheatsheet

Basic Event Listeners

Adding and Removing Listeners

// Add event listener
element.addEventListener('click', handleClick);

// Remove event listener (must be same function reference)
element.removeEventListener('click', handleClick);

// Inline function (can't be removed)
element.addEventListener('click', () => console.log('clicked'));

// Named function (can be removed)
function handleClick(event) {
    console.log('clicked');
}
element.addEventListener('click', handleClick);

Common Event Types

// Mouse events
element.addEventListener('click', handler);
element.addEventListener('dblclick', handler);
element.addEventListener('mouseenter', handler);
element.addEventListener('mouseleave', handler);
element.addEventListener('mousedown', handler);
element.addEventListener('mouseup', handler);
element.addEventListener('mousemove', handler);

// Keyboard events
element.addEventListener('keydown', handler);
element.addEventListener('keyup', handler);
element.addEventListener('keypress', handler);

// Form events
element.addEventListener('submit', handler);
element.addEventListener('change', handler);
element.addEventListener('input', handler);
element.addEventListener('focus', handler);
element.addEventListener('blur', handler);

// Document events
document.addEventListener('DOMContentLoaded', handler);
window.addEventListener('load', handler);
window.addEventListener('resize', handler);
window.addEventListener('scroll', handler);

Event Object Properties

Common Properties

function handleEvent(event) {
    // Event target
    console.log(event.target);        // Element that triggered event
    console.log(event.currentTarget); // Element with listener
    
    // Mouse events
    console.log(event.clientX, event.clientY); // Mouse position
    console.log(event.button);        // Mouse button (0=left, 1=middle, 2=right)
    
    // Keyboard events
    console.log(event.key);           // Key pressed
    console.log(event.code);          // Physical key code
    console.log(event.ctrlKey);       // Ctrl key pressed
    console.log(event.shiftKey);      // Shift key pressed
    console.log(event.altKey);        // Alt key pressed
    
    // Form events
    console.log(event.target.value);  // Input value
    
    // Prevent default behavior
    event.preventDefault();
    
    // Stop event propagation
    event.stopPropagation();
}

Event Bubbling and Capturing

Event Phases

// Bubbling phase (default) - from target to root
element.addEventListener('click', handler); // or handler, false

// Capturing phase - from root to target
element.addEventListener('click', handler, true);

// Example: Parent and child elements
parent.addEventListener('click', () => console.log('Parent clicked'));
child.addEventListener('click', () => console.log('Child clicked'));

// Clicking child logs: "Child clicked", then "Parent clicked"

Stop Propagation

function handleClick(event) {
    console.log('Element clicked');
    event.stopPropagation(); // Prevents bubbling to parent
    // event.stopImmediatePropagation(); // Also stops other listeners on same element
}

Event Delegation

Efficient Event Handling

// ❌ Inefficient: Add listener to each item
const items = document.querySelectorAll('.item');
items.forEach(item => {
    item.addEventListener('click', handleItemClick);
});

// ✅ Efficient: Single listener on parent
document.querySelector('.container').addEventListener('click', (event) => {
    if (event.target.matches('.item')) {
        handleItemClick(event);
    }
});

// Multiple selectors
document.addEventListener('click', (event) => {
    if (event.target.matches('.button, .link, .item')) {
        handleClick(event);
    }
});

Dynamic Content

// Handle dynamically added elements
document.querySelector('.list').addEventListener('click', (event) => {
    if (event.target.classList.contains('delete-btn')) {
        deleteItem(event.target.dataset.id);
    }
});

// Add new items (no need to add listeners)
function addItem(text) {
    const item = document.createElement('li');
    item.innerHTML = `${text} <button class="delete-btn" data-id="${Date.now()}">Delete</button>`;
    document.querySelector('.list').appendChild(item);
}

Form Handling

Form Events

const form = document.querySelector('form');

// Form submission
form.addEventListener('submit', (event) => {
    event.preventDefault();
    const formData = new FormData(form);
    const data = Object.fromEntries(formData);
    console.log(data);
});

// Input validation
const input = document.querySelector('input[type="email"]');
input.addEventListener('input', (event) => {
    const isValid = event.target.checkValidity();
    event.target.classList.toggle('invalid', !isValid);
});

// Real-time validation
input.addEventListener('blur', validateField);
input.addEventListener('input', debounce(validateField, 300));

File Input

const fileInput = document.querySelector('input[type="file"]');

fileInput.addEventListener('change', (event) => {
    const files = event.target.files;
    for (const file of files) {
        console.log('File:', file.name, file.size, file.type);
    }
});

// Drag and drop
const dropZone = document.querySelector('.drop-zone');

dropZone.addEventListener('dragover', (event) => {
    event.preventDefault();
    dropZone.classList.add('dragover');
});

dropZone.addEventListener('drop', (event) => {
    event.preventDefault();
    dropZone.classList.remove('dragover');
    const files = event.dataTransfer.files;
    handleFiles(files);
});

Keyboard Events

Key Handling

// Specific key detection
document.addEventListener('keydown', (event) => {
    if (event.key === 'Enter') {
        submitForm();
    }
    if (event.key === 'Escape') {
        closeModal();
    }
    if (event.ctrlKey && event.key === 's') {
        event.preventDefault();
        saveDocument();
    }
});

// Key combinations
document.addEventListener('keydown', (event) => {
    if (event.ctrlKey && event.shiftKey && event.key === 'S') {
        saveAs();
    }
});

// Input filtering
const numberInput = document.querySelector('input[type="number"]');
numberInput.addEventListener('keypress', (event) => {
    if (!/[0-9]/.test(event.key)) {
        event.preventDefault();
    }
});

Custom Events

Creating and Dispatching

// Create custom event
const customEvent = new CustomEvent('userAction', {
    detail: { action: 'login', userId: 123 },
    bubbles: true,
    cancelable: true
});

// Dispatch event
element.dispatchEvent(customEvent);

// Listen for custom event
element.addEventListener('userAction', (event) => {
    console.log('User action:', event.detail);
});

// Event with data
function triggerEvent(element, eventName, data) {
    const event = new CustomEvent(eventName, {
        detail: data,
        bubbles: true
    });
    element.dispatchEvent(event);
}

Performance Optimization

Debouncing and Throttling

// Debounce function
function debounce(func, delay) {
    let timeoutId;
    return function(...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => func.apply(this, args), delay);
    };
}

// Throttle function
function throttle(func, limit) {
    let inThrottle;
    return function(...args) {
        if (!inThrottle) {
            func.apply(this, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    };
}

// Usage
const debouncedSearch = debounce(searchFunction, 300);
const throttledScroll = throttle(handleScroll, 100);

searchInput.addEventListener('input', debouncedSearch);
window.addEventListener('scroll', throttledScroll);

Memory Management

// Clean up event listeners
class Component {
    constructor() {
        this.boundHandler = this.handleClick.bind(this);
        this.element.addEventListener('click', this.boundHandler);
    }
    
    destroy() {
        this.element.removeEventListener('click', this.boundHandler);
    }
}

// Weak references for cleanup
const listeners = new WeakMap();
function addListenerWithCleanup(element, event, handler) {
    const boundHandler = handler.bind(element);
    listeners.set(element, { event, handler: boundHandler });
    element.addEventListener(event, boundHandler);
}

Common Patterns

Modal Handling

const modal = document.querySelector('.modal');
const openBtn = document.querySelector('.open-modal');
const closeBtn = document.querySelector('.close-modal');

openBtn.addEventListener('click', () => modal.classList.add('open'));
closeBtn.addEventListener('click', () => modal.classList.remove('open'));

// Close on escape
document.addEventListener('keydown', (event) => {
    if (event.key === 'Escape' && modal.classList.contains('open')) {
        modal.classList.remove('open');
    }
});

// Close on outside click
modal.addEventListener('click', (event) => {
    if (event.target === modal) {
        modal.classList.remove('open');
    }
});

Toggle Functionality

// Toggle class
function toggleClass(element, className) {
    element.classList.toggle(className);
}

// Toggle visibility
function toggleVisibility(element) {
    element.style.display = element.style.display === 'none' ? 'block' : 'none';
}

// Usage
button.addEventListener('click', () => toggleClass(menu, 'open'));

Infinite Scroll

let page = 1;
const loadMoreThreshold = 100;

window.addEventListener('scroll', throttle(() => {
    const scrollTop = window.scrollY;
    const windowHeight = window.innerHeight;
    const documentHeight = document.documentElement.scrollHeight;
    
    if (documentHeight - scrollTop - windowHeight < loadMoreThreshold) {
        loadMoreContent(page++);
    }
}, 100));