Docs

18.4-URL-and-History-APIs

11.4 URL and History APIs

Overview

The URL API provides utilities for parsing, constructing, and manipulating URLs. The History API allows manipulation of the browser session history, enabling Single-Page Application (SPA) navigation.

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                          URL Anatomy                             │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                                  │
│   https://user:pass@api.example.com:8080/path/to/page?q=1#sec   │
│   ā”œā”€ā”€ā”€ā”¤  ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤ ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤ ā”œā”€ā”€ā”¤ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤ā”œā”€ā”€ā”¤ā”œā”€ā”€ā”¤   │
│   protocol username        host    port   pathname  search hash  │
│              password                                            │
│                                                                  │
│   ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”   │
│   │ url.protocol  →  'https:'                                │   │
│   │ url.username  →  'user'                                  │   │
│   │ url.password  →  'pass'                                  │   │
│   │ url.hostname  →  'api.example.com'                       │   │
│   │ url.host      →  'api.example.com:8080'                  │   │
│   │ url.port      →  '8080'                                  │   │
│   │ url.pathname  →  '/path/to/page'                         │   │
│   │ url.search    →  '?q=1'                                  │   │
│   │ url.hash      →  '#sec'                                  │   │
│   │ url.origin    →  'https://api.example.com:8080'          │   │
│   │ url.href      →  (full URL)                              │   │
│   ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜   │
│                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

URL Constructor

// Create from full URL
const url = new URL('https://example.com/path?query=value');

// Create with base URL
const url2 = new URL('/api/users', 'https://example.com');
// Result: https://example.com/api/users

// Modify URL parts
url.pathname = '/new-path';
url.hash = '#section';
console.log(url.href);

URLSearchParams

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                     URLSearchParams Methods                       │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                                  │
│   Creating:                                                      │
│   ─────────                                                      │
│   new URLSearchParams()              // Empty                    │
│   new URLSearchParams('?a=1&b=2')    // From string              │
│   new URLSearchParams({ a: '1' })    // From object              │
│   new URLSearchParams([['a','1']])   // From entries             │
│                                                                  │
│   Methods:                                                       │
│   ────────                                                       │
│   .get(name)          │ Get first value                         │
│   .getAll(name)       │ Get all values as array                 │
│   .has(name)          │ Check if param exists                   │
│   .set(name, value)   │ Set value (replaces all)                │
│   .append(name, value)│ Add value (allows duplicates)           │
│   .delete(name)       │ Remove all with name                    │
│   .toString()         │ Serialize to string                     │
│                                                                  │
│   Iteration:                                                     │
│   ──────────                                                     │
│   .keys()             │ Iterator of names                       │
│   .values()           │ Iterator of values                      │
│   .entries()          │ Iterator of [name, value]               │
│   .forEach(fn)        │ Iterate all entries                     │
│                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

URLSearchParams Examples

// Parse current URL query params
const params = new URLSearchParams(window.location.search);

// Get values
const page = params.get('page'); // '1' or null
const tags = params.getAll('tag'); // ['js', 'web']

// Modify
params.set('page', '2');
params.append('sort', 'date');
params.delete('old');

// Build URL
const url = new URL('https://api.example.com/search');
url.search = params.toString();

History API

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                      History API Overview                         │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                                  │
│   Browser History Stack:                                         │
│   ──────────────────────                                        │
│   ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”                 │
│   │ Page 1  →  Page 2  →  Page 3  →  Page 4   │  ← Current      │
│   ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜                 │
│                                ↑                                 │
│                           history.back()                         │
│                         history.forward()                        │
│                            history.go(n)                         │
│                                                                  │
│   Properties:                                                    │
│   ───────────                                                   │
│   history.length       │ Number of entries in history           │
│   history.state        │ State object for current entry         │
│   history.scrollRestoration │ 'auto' or 'manual'                │
│                                                                  │
│   Methods:                                                       │
│   ────────                                                      │
│   history.back()       │ Go back one page                       │
│   history.forward()    │ Go forward one page                    │
│   history.go(n)        │ Go n pages (+ forward, - back)         │
│   history.pushState()  │ Add new entry                          │
│   history.replaceState()│ Replace current entry                 │
│                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

pushState vs replaceState

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                  pushState vs replaceState                        │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                                  │
│   pushState(state, title, url)                                   │
│   ────────────────────────────                                  │
│   • Adds NEW entry to history stack                              │
│   • Back button returns to previous page                         │
│   • history.length increases                                     │
│                                                                  │
│   Before: [Page1, Page2, Page3*]                                 │
│   After:  [Page1, Page2, Page3, NewPage*]                        │
│                                                                  │
│   replaceState(state, title, url)                                │
│   ───────────────────────────────                               │
│   • REPLACES current entry in history                            │
│   • Back button skips replaced page                              │
│   • history.length stays same                                    │
│                                                                  │
│   Before: [Page1, Page2, Page3*]                                 │
│   After:  [Page1, Page2, NewPage*]                               │
│                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

State Object

// pushState(stateObject, title, url)
history.pushState(
  { page: 'products', id: 123 }, // State data (serializable)
  '', // Title (most browsers ignore)
  '/products/123' // New URL (same origin only)
);

// Access state later
console.log(history.state); // { page: 'products', id: 123 }

// replaceState works the same
history.replaceState({ updated: true }, '', '/products/123/edit');

popstate Event

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                      popstate Event                               │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                                  │
│   Fired when:                                                    │
│   • User clicks back/forward buttons                             │
│   • history.back(), forward(), go() called                       │
│                                                                  │
│   NOT fired when:                                                │
│   • pushState() or replaceState() called                         │
│                                                                  │
│   window.addEventListener('popstate', (event) => {               │
│       console.log('State:', event.state);                        │
│       console.log('URL:', location.href);                        │
│                                                                  │
│       // Handle navigation                                       │
│       if (event.state?.page === 'products') {                    │
│           showProductPage(event.state.id);                       │
│       }                                                          │
│   });                                                            │
│                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Location Object

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                     window.location                               │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                                  │
│   Properties (read/write):                                       │
│   ────────────────────────                                      │
│   location.href       │ Full URL (setting navigates)            │
│   location.protocol   │ 'http:' or 'https:'                     │
│   location.host       │ hostname:port                           │
│   location.hostname   │ Domain name                             │
│   location.port       │ Port number                             │
│   location.pathname   │ Path after domain                       │
│   location.search     │ Query string with ?                     │
│   location.hash       │ Fragment with #                         │
│                                                                  │
│   Methods:                                                       │
│   ────────                                                      │
│   location.assign(url)  │ Navigate (adds to history)            │
│   location.replace(url) │ Navigate (replaces history)           │
│   location.reload()     │ Reload current page                   │
│   location.toString()   │ Returns href                          │
│                                                                  │
│   Navigation Examples:                                           │
│   ────────────────────                                          │
│   location.href = '/new-page';        // Navigate                │
│   location = '/new-page';             // Same as above           │
│   location.hash = '#section';         // Change hash only        │
│   location.search = '?page=2';        // Change query            │
│                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Common URL Patterns

Building Query Strings

function buildQueryString(params) {
  const searchParams = new URLSearchParams();

  Object.entries(params).forEach(([key, value]) => {
    if (value === undefined || value === null) return;

    if (Array.isArray(value)) {
      value.forEach((v) => searchParams.append(key, v));
    } else {
      searchParams.set(key, value);
    }
  });

  return searchParams.toString();
}

// Usage
const query = buildQueryString({
  search: 'hello world',
  tags: ['js', 'web'],
  page: 1,
});
// Result: search=hello+world&tags=js&tags=web&page=1

Parsing URLs

function parseURL(urlString) {
  const url = new URL(urlString);
  const params = Object.fromEntries(url.searchParams);

  return {
    protocol: url.protocol,
    host: url.host,
    pathname: url.pathname,
    params,
    hash: url.hash.slice(1),
  };
}

SPA Routing Pattern

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                    SPA Router Pattern                             │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                                  │
│   1. Define Routes                                               │
│   ────────────────                                              │
│   const routes = {                                               │
│       '/': HomePage,                                             │
│       '/products': ProductsPage,                                 │
│       '/products/:id': ProductDetailPage                         │
│   };                                                             │
│                                                                  │
│   2. Handle Navigation                                           │
│   ────────────────────                                          │
│   function navigate(path, state = {}) {                          │
│       history.pushState(state, '', path);                        │
│       renderCurrentRoute();                                      │
│   }                                                              │
│                                                                  │
│   3. Listen for Back/Forward                                     │
│   ──────────────────────────                                    │
│   window.addEventListener('popstate', () => {                    │
│       renderCurrentRoute();                                      │
│   });                                                            │
│                                                                  │
│   4. Match and Render                                            │
│   ───────────────────                                           │
│   function renderCurrentRoute() {                                │
│       const path = location.pathname;                            │
│       const Component = matchRoute(path);                        │
│       Component.render();                                        │
│   }                                                              │
│                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

URL Encoding

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                     URL Encoding Methods                          │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                                  │
│   encodeURI(str)                                                 │
│   ──────────────                                                │
│   • Encodes full URI                                             │
│   • Preserves: : / ? # [ ] @ ! $ & ' ( ) * + , ; =              │
│   • Use for: Complete URLs                                       │
│                                                                  │
│   encodeURIComponent(str)                                        │
│   ───────────────────────                                       │
│   • Encodes URI component                                        │
│   • Encodes everything except: A-Z a-z 0-9 - _ . ! ~ * ' ( )    │
│   • Use for: Query parameters, path segments                     │
│                                                                  │
│   Examples:                                                      │
│   ─────────                                                     │
│   encodeURI('https://example.com/path?name=John Doe')           │
│   → 'https://example.com/path?name=John%20Doe'                  │
│                                                                  │
│   encodeURIComponent('name=John&age=30')                         │
│   → 'name%3DJohn%26age%3D30'                                     │
│                                                                  │
│   // Building URL safely                                         │
│   const param = encodeURIComponent(userInput);                   │
│   const url = `https://api.com/search?q=${param}`;               │
│                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

hashchange Event

// Listen for hash changes
window.addEventListener('hashchange', (event) => {
  console.log('Old URL:', event.oldURL);
  console.log('New URL:', event.newURL);
  console.log('New hash:', location.hash);

  // Handle hash-based navigation
  handleHashRoute(location.hash);
});

// Change hash (triggers event)
location.hash = '#/products';

// Read hash
const hash = location.hash.slice(1); // Remove # prefix

Best Practices

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                      Best Practices                               │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                                  │
│   URL Construction:                                              │
│   ─────────────────                                             │
│   āœ“ Use URL constructor for parsing                              │
│   āœ“ Use URLSearchParams for query strings                        │
│   āœ“ Always encode user input with encodeURIComponent             │
│   āœ“ Validate URLs before using                                   │
│                                                                  │
│   History API:                                                   │
│   ────────────                                                  │
│   āœ“ Keep state objects small and serializable                    │
│   āœ“ Always handle popstate for SPAs                              │
│   āœ“ Use replaceState for redirects                               │
│   āœ“ Consider scroll position management                          │
│                                                                  │
│   Security:                                                      │
│   ─────────                                                     │
│   āœ“ Validate URLs before navigation                              │
│   āœ“ Avoid using user input in location.href directly             │
│   āœ“ Check origin before processing postMessage                   │
│                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Browser Compatibility

FeatureChromeFirefoxSafariEdge
URL32+26+7+12+
URLSearchParams49+44+10.1+17+
history.pushState5+4+5+12+
popstate event5+4+5+12+

Key Takeaways

  1. •URL API - Parse and construct URLs safely
  2. •URLSearchParams - Easy query string manipulation
  3. •pushState - Add to history without reload
  4. •replaceState - Modify current history entry
  5. •popstate - Handle back/forward navigation
  6. •location - Navigate and read current URL
  7. •Always encode - Use encodeURIComponent for user input
.4 URL And History APIs - JavaScript Tutorial | DeepML