この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:6
ページ更新者:guest
更新日時:2026-06-11 07:07:02

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

この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><strong>HTML5 History API</strong> = URL を変えずにブラウザ履歴を操作する API。SPA ルーティングの土台</li><li><code>history.pushState(state, "", url)</code> で履歴を追加(ページリロード無し)</li><li><code>history.replaceState()</code> は履歴を置き換え(戻れない)</li><li>戻る/進むで <code>popstate</code> イベントが発火 → そこで View を切替</li><li><code>location.hash</code> 方式(古典)と比べ URL がきれいだが、サーバ側で <strong>SPA フォールバック</strong>(全パスを index.html に返す)が必要</li></ul></td></tr></table>
<h2>History API とは</h2>

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

<h2>主要メソッド一覧</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>API</th><th>動作</th><th>履歴に追加</th></tr></thead>
<tbody>
<tr><td><code>history.pushState(state, title, url)</code></td><td>新エントリ追加</td><td>○</td></tr>
<tr><td><code>history.replaceState(state, title, url)</code></td><td>現エントリを置換</td><td>×</td></tr>
<tr><td><code>history.back()</code></td><td>1 つ戻る(戻るボタン同等)</td><td>-</td></tr>
<tr><td><code>history.forward()</code></td><td>1 つ進む</td><td>-</td></tr>
<tr><td><code>history.go(n)</code></td><td>n エントリ移動(負数で戻る)</td><td>-</td></tr>
<tr><td><code>history.length</code></td><td>現セッションの履歴数</td><td>-</td></tr>
<tr><td><code>history.state</code></td><td>現エントリの state オブジェクト</td><td>-</td></tr>
<tr><td><code>history.scrollRestoration</code></td><td>戻り時のスクロール位置復元(<code>auto</code>/<code>manual</code>)</td><td>-</td></tr>
</tbody>
</table>

<h2>pushState / popstate の最小例</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><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(&apos;view&apos;).textContent = path + &apos; Page&apos;;
}

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

// 戻る/進むで popstate
window.addEventListener(&apos;popstate&apos;, (e) => {
  const path = (e.state && e.state.path) || location.pathname;
  render(path);
});
</script></code></pre>
<h2>pushState と replaceState の違い</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#fff3e0"><th>用途</th><th>選択</th></tr></thead>
<tbody>
<tr><td>通常のページ遷移</td><td><code>pushState</code></td></tr>
<tr><td>クエリ更新(フィルタ変更等)で戻る対象にしたくない</td><td><code>replaceState</code></td></tr>
<tr><td>ログイン後のリダイレクト(ログイン画面を履歴に残さない)</td><td><code>replaceState</code></td></tr>
<tr><td>モーダルの開閉を URL に反映</td><td>状況次第(戻るで閉じたいなら <code>pushState</code>)</td></tr>
</tbody>
</table>

<h2>location.hash 方式との比較</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// 古典: ハッシュベース(HTML5 以前)
location.hash = &apos;#/about&apos;;
window.addEventListener(&apos;hashchange&apos;, () => {
  console.log(location.hash);
});
// URL: https://example.com/#/about

// モダン: History API
history.pushState(null, &apos;&apos;, &apos;/about&apos;);
// URL: https://example.com/about</code></pre>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>比較</th><th>hash 方式</th><th>History API</th></tr></thead>
<tbody>
<tr><td>URL 見た目</td><td><code>#</code> 入りで不格好</td><td>通常 URL でクリーン</td></tr>
<tr><td>SEO</td><td>通常クロール対象外</td><td>SSR / プリレンダリングで対応可</td></tr>
<tr><td>サーバ設定</td><td>不要</td><td>★ 全パスを index.html へフォールバックが必要</td></tr>
<tr><td>古い IE 対応</td><td>IE6+ で動く</td><td>IE10+</td></tr>
</tbody>
</table>

<h2>SPA フォールバックの設定例</h2>

<p>History API で <code>/about</code> という URL に切り替えても、ブラウザをリロードすると<strong>サーバが本当に <code>/about</code> を返そうとして 404</strong> になります。これを防ぐためサーバで「全パスを <code>index.html</code> に返す」設定を入れます。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-nginx"># Nginx
location / {
  try_files $uri $uri/ /index.html;
}</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-apache"># Apache (.htaccess)
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// Express
const path = require(&apos;path&apos;);
app.use(express.static(&apos;dist&apos;));
app.get(&apos;*&apos;, (_req, res) => {
  res.sendFile(path.join(__dirname, &apos;dist&apos;, &apos;index.html&apos;));
});</code></pre>
<h2>URL / URLSearchParams との連携</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// 現在の URL からクエリ取得
const params = new URLSearchParams(location.search);
const page = params.get(&apos;page&apos;) || &apos;1&apos;;

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

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

<p>「戻る」操作で前ページのスクロール位置を復元するかをコントロールできます。SPA では自前で復元したい場合があるので <code>manual</code> にする運用が一般的です。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">if (&apos;scrollRestoration&apos; in history) {
  history.scrollRestoration = &apos;manual&apos;;
}

// 自前でスクロール位置を保存
window.addEventListener(&apos;beforeunload&apos;, () => {
  sessionStorage.setItem(&apos;scroll:&apos; + location.pathname, scrollY);
});
window.addEventListener(&apos;load&apos;, () => {
  const y = sessionStorage.getItem(&apos;scroll:&apos; + location.pathname);
  if (y) window.scrollTo(0, +y);
});</code></pre>
<h2>Router フレームワークの内部</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e8f5e9"><th>フレームワーク</th><th>History API の使い方</th></tr></thead>
<tbody>
<tr><td>Vue Router (createWebHistory)</td><td><code>history.pushState</code> + <code>popstate</code> リスナ</td></tr>
<tr><td>React Router (BrowserRouter)</td><td>history パッケージ経由で History API</td></tr>
<tr><td>Next.js App Router</td><td>独自 Router、Soft Navigation に History API</td></tr>
<tr><td>SvelteKit</td><td>fetch + History API + クライアントナビ</td></tr>
<tr><td>Astro View Transitions</td><td>History API + ViewTransition API</td></tr>
</tbody>
</table>

<h2>FAQ</h2>

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

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

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