The URL hash — the fragment identifier after the # — serves several purposes: in-page navigation, client-side routing, and preserving application state. JavaScript provides straightforward APIs to read and monitor hash changes.
Reading the Current Hash
Access the hash using window.location.hash:
const hash = window.location.hash;
console.log(hash); // e.g., "#section-about"
This returns the hash including the # character. To get the hash without the #:
const hash = window.location.hash.slice(1);
console.log(hash); // e.g., "section-about"
Detecting Hash Changes
Use the hashchange event to respond when the URL fragment changes:
window.addEventListener('hashchange', (event) => {
console.log('Old URL:', event.oldURL);
console.log('New URL:', event.newURL);
console.log('Current hash:', window.location.hash);
});
This fires when:
- A user clicks a link with an
href="#anchor" - The user uses browser back/forward buttons
- JavaScript programmatically changes the hash via
window.location.hash = '#new-hash'
Programmatically Changing the Hash
Modify the hash directly:
window.location.hash = '#section-contact';
Or use the History API for more control:
window.history.pushState(null, '', '#section-contact');
The pushState approach doesn’t trigger hashchange, so listen for popstate instead if you need to react to navigation:
window.addEventListener('popstate', (event) => {
console.log('Navigated via back/forward');
});
Practical Example: Simple Hash-Based Navigation
function handleNavigation() {
const hash = window.location.hash.slice(1) || 'home';
// Hide all sections
document.querySelectorAll('[data-section]').forEach(el => {
el.hidden = true;
});
// Show active section
const activeSection = document.querySelector(`[data-section="${hash}"]`);
if (activeSection) {
activeSection.hidden = false;
}
}
// Initialize on page load
handleNavigation();
// Listen for hash changes
window.addEventListener('hashchange', handleNavigation);
HTML structure:
<nav>
<a href="#home">Home</a>
<a href="#about">About</a>
<a href="#contact">Contact</a>
</nav>
<section data-section="home">Home content</section>
<section data-section="about">About content</section>
<section data-section="contact">Contact content</section>
When to Use Hashes vs. History API
Use hashes for:
- Simple single-page applications without a framework
- Static documentation sites with anchor-based navigation
- Backwards compatibility with older browsers (though hashes work in all modern browsers)
Use the History API (pushState/replaceState) for:
- Production SPAs with frameworks like React, Vue, or Svelte
- Cleaner URLs (e.g.,
/aboutinstead of/#/about) - More granular state management
Modern frameworks like Next.js, Nuxt, and SvelteKit handle routing automatically, so you rarely interact with the hash API directly in production. However, understanding these mechanics remains essential for simple projects, browser extensions, and debugging.
