- カテゴリー
- Web標準API
- 公開日
- 2026.09.26
Contents
はじめに
従来の小さなSPAルーターは、リンクのclick、history.pushState()、popstateを別々に扱う必要がありました。Navigation APIは、ナビゲーションをnavigateイベントへ集約し、intercept()内で描画処理を実行できます。
この記事ではHomeとAboutを切り替える最小ルーターを作ります。フレームワークのルーターを置き換えることが目的ではなく、Web標準の動作を理解する記事です。
SPAとルーターとは
SPA(Single Page Application)は、リンクを押すたびにHTML全体を読み直すのではなく、JavaScriptで必要な表示だけを切り替えるWebアプリです。ルーターは、/や/aboutというURLと表示内容を対応付けます。
HTMLとJavaScriptの基本を学び、ファイル作成とターミナル操作ができる方向けです。完成後は、HomeとAboutをクリックするとURLと本文が切り替わり、ブラウザの戻る・進むも使えます。Navigation API非対応時は通常のページ遷移へ戻します。ただし本文の描画にはJavaScriptが必要です。JavaScript無効時にも本文を届けるには、サーバー側で各ページのHTMLを返す別の仕組みが必要です。
必要なファイルと起動方法
Node.js 22.12以上(この例では24系)とnpm、Chromeなどのブラウザを用意します。同じフォルダーにindex.htmlとapp.jsを作り、開発サーバーから配信します。
mkdir navigation-demo
cd navigation-demo
npm init -y
npm install --save-dev vite@7.3.6
下記の2ファイルを保存したらnpx viteを実行し、表示されたローカルURL(通常はhttp://localhost:5173)を開きます。終了はターミナルでCtrl+Cです。Viteの開発サーバーはSPAのフォールバックに対応します。本番配信でも/aboutへindex.htmlを返す設定が必要です。
HTML
<!doctype html>
<html lang="ja">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Navigation API Demo</title>
<style>
body { max-width: 720px; margin: 32px auto; padding: 0 16px; font-family: system-ui; }
nav { display: flex; gap: 16px; }
</style>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<main id="app"><p>ページを読み込んでいます。</p></main>
<noscript><p>このデモの本文表示にはJavaScriptが必要です。</p></noscript>
<p id="error" role="alert"></p>
<script type="module" src="/app.js"></script>
</html>
開発サーバーは/aboutを直接開いても同じHTMLを返すように設定します。これを行わないと、再読み込み時に404になります。
ルーター
次をapp.jsとして保存します。innerHTMLへ入れるのは、コード内で定義した固定文字列だけです。URLの値や利用者入力をそのまま連結するとXSSの原因になります。
const app = document.querySelector("#app");
const errorBox = document.querySelector("#error");
const routes = new Map([
["/", () => "<h1>Home</h1><p>トップページです。</p>"],
["/about", () => "<h1>About</h1><p>このサイトについて。</p>"],
]);
async function render(url, signal) {
signal?.throwIfAborted();
const view = routes.get(url.pathname);
app.innerHTML = view ? view() : "<h1>404</h1>";
document.title = view ? `Demo | ${url.pathname}` : "404 | Demo";
}
// 初回ロードではnavigateイベントが発生しないため、明示的に描画する。
await render(new URL(location.href));
if ("navigation" in window) {
navigation.addEventListener("navigate", (event) => {
const destination = new URL(event.destination.url);
if (!event.canIntercept) return;
if (destination.origin !== location.origin) return;
if (event.hashChange || event.downloadRequest !== null) return;
if (event.formData !== null || event.navigationType === "reload") return;
event.intercept({
async handler() {
errorBox.textContent = "";
try {
await render(destination, event.signal);
} catch (error) {
if (error.name !== "AbortError") {
errorBox.textContent = "画面の読み込みに失敗しました。";
throw error;
}
}
},
});
});
}
別ページへの遷移が始まると前のevent.signalがabortされることがあります。非同期取得を追加する場合はfetch(url, { signal: event.signal })へ渡し、取得後・描画直前にもsignal.throwIfAborted()を呼びます。この例の描画は固定文字列で、非同期通信はありません。フォーム送信と再読み込みは横取りせず、元のブラウザ動作に任せます。
非対応ブラウザのフォールバック
Navigation APIがないブラウザでもJavaScript自体が動けば、通常遷移でサーバーからHTMLを読み直し、初回のrender()が本文を描画します。これは「JavaScript不要」という意味ではありません。以下は分岐を理解するための補足で、完成コードに追加しなくても通常遷移へ戻ります。
if (!("navigation" in window)) {
console.info("通常のページ遷移を使用します");
}
リンクをhref="#"にしてJavaScriptだけで動かす設計は、非対応時やスクリプト失敗時に壊れるため避けます。
動作確認
- リンククリックでページ全体を再読込せず描画が変わる
- 戻る・進むで表示とURLが一致する
/aboutを直接開いて表示できる- 外部リンクとdownloadリンクを横取りしない
- 高速に連打しても古い描画が残らない
- 不明なURLで404表示になる
期待結果は、Homeで「トップページです。」、Aboutで「このサイトについて。」です。/missingでは画面に404と出ますが、SPAフォールバックが返すHTTPステータスは200です。本番で正しいHTTP 404を返すにはサーバー側のルーティングも必要です。非対応分岐を試すときは検証用コピーで条件をif (false)へ置き換え、リンクが通常遷移して表示が復元するか確かめます。これは非対応ブラウザ実機の検証とは区別します。
制約
初回ロードではnavigateイベントが発生しないため初期描画が必要です。APIが扱う履歴は現在のbrowsing contextかつ同一オリジンの範囲です。SSR、データキャッシュ、ネストしたroute、認証guardまで必要なアプリでは、成熟したルーターライブラリの方が適する場合があります。
よくあるトラブル
- リンクを押すとページ全体が再読み込みされる:ブラウザがNavigation APIへ対応しているか、コンソールに例外がないか確認する
/aboutを再読み込みすると404になる:開発・配信サーバーのSPAフォールバックを設定する- 戻る操作で本文が変わらない:
navigateイベント内で同じrender()を呼んでいるか確認する - 外部リンクまで横取りする:遷移先の
originを確認してからintercept()する - 画面を連打すると古い内容が出る:
event.signalをfetch()や描画処理へ渡す
関連記事
まとめ
Navigation APIは、SPAのナビゲーション入口をnavigateへまとめます。canIntercept、origin、hash、downloadを確認し、AbortSignalと初回描画を忘れないことが最小実装の要点です。


コメント