7. Event Handling
JavaScript events are the backbone of interactive web applications. Mastering them means you can build dynamic, user-friendly interfaces. Here’s a deep dive into the most important event concepts, with practical examples, best practices, and real-world tips.
7.1 Core Events
addEventListener() – The Modern Way
Attaches an event handler to an element. You can add multiple listeners of the same type.
const button = document.getElementById('myBtn');
button.addEventListener('click', function() {
alert('Button clicked!');
});
// Best practice: Use named functions for easier removal
function handleClick() {
alert('Clicked!');
}
button.addEventListener('click', handleClick);
// Remove listener
button.removeEventListener('click', handleClick);
Tip: Avoid using onclick attributes in HTML. Use addEventListener for separation of concerns and flexibility.
Event Bubbling & Delegation
Events bubble up from the target element to its ancestors. This enables event delegation—a powerful pattern for handling many similar elements efficiently.
<ul id="list">
<li>Item 1</li>
<li>Item 2</li>
</ul>
<script>
document.getElementById('list').addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
alert('Clicked: ' + e.target.textContent);
}
});
</script>
Best Practice: Use delegation for lists, tables, or dynamic content.
event.target vs event.currentTarget
event.target is the element that triggered the event. event.currentTarget is the element the handler is attached to.
document.getElementById('list').addEventListener('click', function(e) {
console.log('target:', e.target);
console.log('currentTarget:', e.currentTarget);
});
Gotcha: If you attach a handler to a parent, event.target may be a child element.
7.2 Event Types
Scroll Events (with Debouncing)
Scroll events fire rapidly. Use debouncing to avoid performance issues.
let timeout;
window.addEventListener('scroll', () => {
clearTimeout(timeout);
timeout = setTimeout(() => {
console.log('User finished scrolling!');
}, 100);
});
Mouse Events
const box = document.getElementById('box');
box.addEventListener('mouseenter', () => box.classList.add('hover'));
box.addEventListener('mouseleave', () => box.classList.remove('hover'));
box.addEventListener('mousedown', () => box.style.background = 'red');
box.addEventListener('mouseup', () => box.style.background = '');
box.addEventListener('dblclick', () => alert('Double click!'));
Keyboard Events (Shortcuts)
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
alert('Save shortcut triggered!');
}
});
Tip: Use keyup for actions after key release, keydown for instant response.
7.3 Forms & Inputs
Accessing Form Data (Best Practice)
<form id="loginForm">
<input name="username" required />
<input name="password" type="password" required />
<button>Login</button>
</form>
<script>
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.target));
alert('Username: ' + data.username + '\nPassword: ' + data.password);
});
</script>
Validation (Pattern, Required, Custom)
<input id="age" type="number" min="1" max="120" required />
<span id="error"></span>
<script>
const input = document.getElementById('age');
const error = document.getElementById('error');
input.addEventListener('input', function() {
if (!input.checkValidity()) {
error.textContent = 'Please enter a valid age (1-120)';
} else {
error.textContent = '';
}
});
</script>
preventDefault()
Prevents the default browser action (like submitting a form or following a link).
document.querySelector('a').addEventListener('click', function(e) {
e.preventDefault();
alert('Link click prevented!');
});
onsubmit and onchange (Inline vs. JS)
Inline:
<form onsubmit="return false;">...</form>
<input onchange="console.log('Input changed!')" />
Best Practice: Use addEventListener in JS for maintainability.
7.4 Browser Events
DOMContentLoaded (Best for Setup)
Fires when the initial HTML is loaded and parsed.
document.addEventListener('DOMContentLoaded', () => {
// Safe to access DOM elements here
console.log('DOM fully loaded!');
});
load (Wait for Images/Assets)
Fires when the whole page (including images) is loaded.
window.addEventListener('load', () => {
console.log('Page fully loaded!');
});
resize (Responsive UIs)
window.addEventListener('resize', () => {
document.body.textContent = `Window: ${window.innerWidth} x ${window.innerHeight}`;
});
scroll (Sticky Headers, Lazy Loading)
window.addEventListener('scroll', () => {
if (window.scrollY > 100) {
document.body.classList.add('scrolled');
} else {
document.body.classList.remove('scrolled');
}
});
Performance Tip: For scroll/resize, use debouncing or throttling to avoid performance issues.