Vanilla JavaScript
Modern JavaScript is powerful enough for most web applications without a framework. Here's how to write it well.
DOM Querying
// Single element
const hero = document.querySelector('.hero');
// Multiple elements (returns NodeList — iterable)
const cards = document.querySelectorAll('.card');
cards.forEach(card => card.classList.add('visible'));
// Scoped queries
const sidebar = document.querySelector('.sidebar');
const links = sidebar.querySelectorAll('a');
Event Delegation
Instead of attaching listeners to every element, use delegation:
// BAD: listener per button
document.querySelectorAll('.delete-btn').forEach(btn => {
btn.addEventListener('click', handleDelete);
});
// GOOD: single listener on parent
document.querySelector('.item-list').addEventListener('click', (e) => {
const btn = e.target.closest('.delete-btn');
if (!btn) return;
const itemId = btn.dataset.id;
handleDelete(itemId);
});
This handles dynamically added elements automatically.
Fetch API
async function loadPosts(category) {
const response = await fetch(`/api/posts/?category=${category}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
// POST with CSRF (Django)
async function submitForm(data) {
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
const response = await fetch('/api/submit/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken,
},
body: JSON.stringify(data),
});
return response.json();
}
Custom Elements
Build reusable components with Web Components:
class CopyButton extends HTMLElement {
connectedCallback() {
this.innerHTML = `<button class="copy-btn">Copy</button>`;
this.querySelector('button').addEventListener('click', () => {
navigator.clipboard.writeText(this.dataset.text);
this.querySelector('button').textContent = 'Copied!';
setTimeout(() => {
this.querySelector('button').textContent = 'Copy';
}, 2000);
});
}
}
customElements.define('copy-button', CopyButton);
Intersection Observer
Lazy-load content or trigger animations on scroll:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-in');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.animate-on-scroll').forEach(el => {
observer.observe(el);
});