3.

HTML5 History API(pushState / popstate / SPA ルーティング)完全ガイド

編集
この記事の要点
  • HTML5 History API = URL を変えずにブラウザ履歴を操作する API。SPA ルーティングの土台
  • history.pushState(state, "", url) で履歴を追加(ページリロード無し)
  • history.replaceState() は履歴を置き換え(戻れない)
  • 戻る/進むで popstate イベントが発火 → そこで View を切替
  • location.hash 方式(古典)と比べ URL がきれいだが、サーバ側で SPA フォールバック(全パスを index.html に返す)が必要

History API とは

HTML5 History API は、ブラウザの戻る/進む履歴を JavaScript から操作する API です。ページをリロードせずに URL を変更できるため、SPA(Single Page Application)のルーティング機構の中核として広く使われています。Vue Router、React Router、Next.js Client Router、SvelteKit などのフレームワークも内部では History API を呼んでいます。

主要メソッド一覧

API動作履歴に追加
history.pushState(state, title, url)新エントリ追加
history.replaceState(state, title, url)現エントリを置換×
history.back()1 つ戻る(戻るボタン同等)-
history.forward()1 つ進む-
history.go(n)n エントリ移動(負数で戻る)-
history.length現セッションの履歴数-
history.state現エントリの state オブジェクト-
history.scrollRestoration戻り時のスクロール位置復元(auto/manual-

pushState / popstate の最小例

<nav>
  <a href="/home"  data-link>Home</a>
  <a href="/about" data-link>About</a>
</nav>
<main id="view">Home Page</main>

<script>
function render(path) {
  document.getElementById('view').textContent = path + ' Page';
}

// リンククリックで pushState
document.querySelectorAll('[data-link]').forEach(a => {
  a.addEventListener('click', (e) => {
    e.preventDefault();
    const url = a.getAttribute('href');
    history.pushState({ path: url }, '', url);
    render(url);
  });
});

// 戻る/進むで popstate
window.addEventListener('popstate', (e) => {
  const path = (e.state && e.state.path) || location.pathname;
  render(path);
});
</script>

pushState と replaceState の違い

用途選択
通常のページ遷移pushState
クエリ更新(フィルタ変更等)で戻る対象にしたくないreplaceState
ログイン後のリダイレクト(ログイン画面を履歴に残さない)replaceState
モーダルの開閉を URL に反映状況次第(戻るで閉じたいなら pushState

location.hash 方式との比較

// 古典: ハッシュベース(HTML5 以前)
location.hash = '#/about';
window.addEventListener('hashchange', () => {
  console.log(location.hash);
});
// URL: https://example.com/#/about

// モダン: History API
history.pushState(null, '', '/about');
// URL: https://example.com/about
比較hash 方式History API
URL 見た目# 入りで不格好通常 URL でクリーン
SEO通常クロール対象外SSR / プリレンダリングで対応可
サーバ設定不要★ 全パスを index.html へフォールバックが必要
古い IE 対応IE6+ で動くIE10+

SPA フォールバックの設定例

History API で /about という URL に切り替えても、ブラウザをリロードするとサーバが本当に /about を返そうとして 404 になります。これを防ぐためサーバで「全パスを index.html に返す」設定を入れます。

# Nginx
location / {
  try_files $uri $uri/ /index.html;
}
# Apache (.htaccess)
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
// Express
const path = require('path');
app.use(express.static('dist'));
app.get('*', (_req, res) => {
  res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});

URL / URLSearchParams との連携

// 現在の URL からクエリ取得
const params = new URLSearchParams(location.search);
const page = params.get('page') || '1';

// クエリだけ書き換え(履歴に追加せず replace)
params.set('page', '2');
history.replaceState(null, '', '?' + params.toString());

// URL オブジェクトで安全に組み立て
const url = new URL(location.href);
url.pathname = '/items';
url.searchParams.set('sort', 'asc');
history.pushState(null, '', url);

scrollRestoration

「戻る」操作で前ページのスクロール位置を復元するかをコントロールできます。SPA では自前で復元したい場合があるので manual にする運用が一般的です。

if ('scrollRestoration' in history) {
  history.scrollRestoration = 'manual';
}

// 自前でスクロール位置を保存
window.addEventListener('beforeunload', () => {
  sessionStorage.setItem('scroll:' + location.pathname, scrollY);
});
window.addEventListener('load', () => {
  const y = sessionStorage.getItem('scroll:' + location.pathname);
  if (y) window.scrollTo(0, +y);
});

Router フレームワークの内部

フレームワークHistory API の使い方
Vue Router (createWebHistory)history.pushState + popstate リスナ
React Router (BrowserRouter)history パッケージ経由で History API
Next.js App Router独自 Router、Soft Navigation に History API
SvelteKitfetch + History API + クライアントナビ
Astro View TransitionsHistory API + ViewTransition API

FAQ

Q: pushState の第 2 引数 title は何を入れる?
A: ほとんどのブラウザで無視されます。空文字 "" で OK。document.title を別途代入してください。

Q: popstate が pushState 直後にも発火する?
A: 発火しません。popstateユーザの戻る/進む操作と history.back() 等のメソッド呼び出しでのみ発火します。

Q: 別オリジンに pushState できる?
A: できません。同一オリジン内のパス変更のみ許可。クロスオリジンは location.href 代入で通常遷移。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. Canvas API
  2. Drag and Drop API
  3. History API
  4. WebStorage API
  5. File API
  6. System Information API

最近更新/作成されたページ