Skip to main content

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.