JavaScript | 1 日 120 分 × 7 日アプリ学習:SPA風タブ切り替えアプリ

Web APP JavaScript
スポンサーリンク

1日目のゴールと今日やること

1日目のテーマは
URL のハッシュ(#)を使って、SPA っぽいタブ切り替えを実現する」ことです。

“SPA 風”というのは、
ページ全体をリロードせずに、
タブを切り替えるだけで画面が変わっていくように見せる、という意味です。

今日のゴールは、ざっくり言うとこうです。

タブをクリックすると表示が切り替わる。
ブラウザの戻る / 進むでタブ状態が戻る。
再読み込みしても、さっき見ていたタブが復元される。

これを「URL の hash」と「状態と表示の分離」という考え方で作っていきます。


まずは「URL の hash」とは何かを理解する

hash ってどこのこと?

ブラウザの URL を見ると、こんな形があります。

https://example.com/app.html#profile

この #profile の部分が hash(ハッシュ) です。

サーバーには送られず、
ブラウザの中だけで使われる「おまけ情報」のようなものです。

なぜ hash を使うと SPA っぽくなるのか

ページ全体をリロードせずに
「今どの画面を見ているか」を URL に表現できるからです。

#home → ホームタブ
#profile → プロフィールタブ
#settings → 設定タブ

こうしておくと、

タブを切り替えるたびに hash を変える。
ブラウザの戻る / 進むで hash が変わる。
hash が変わったら、それに応じて表示を切り替える。

という流れが作れます。


今日作る「SPA風タブ切り替えアプリ」のイメージ

HTML のイメージ

<nav class="tabs">
  <a href="#home" data-tab="home">ホーム</a>
  <a href="#profile" data-tab="profile">プロフィール</a>
  <a href="#settings" data-tab="settings">設定</a>
</nav>

<section class="view" data-view="home">ホーム画面の内容</section>
<section class="view" data-view="profile">プロフィール画面の内容</section>
<section class="view" data-view="settings">設定画面の内容</section>

ポイントは、
「タブ」と「表示する画面」を
data-tabdata-view で紐づけていることです。

タブは <a href="#home"> のように hash を持っています。
画面は data-view="home" のように「名前」を持っています。


状態と表示を分けて考える、という超重要な発想

よくある“やりがち”な書き方

初心者のうちは、ついこう書きがちです。

タブをクリックしたら、その場で display: none を切り替える。
どのタブが選ばれているかは DOM を見て判断する。

これでも動きますが、
「状態」と「表示」がごちゃごちゃになります。

今日の方針

状態(今どのタブが選ばれているか)は
「URL の hash」と「JavaScript の変数」で管理する。

表示(どの画面を見せるか)は
「状態をもとに DOM を更新する」だけにする。

この分離ができると、
コードが一気に読みやすく、壊れにくくなります。


タブの「状態」を hash から読み取る

今の hash を取得する

function getCurrentTabFromHash() {
  const hash = window.location.hash; // 例: "#home"
  if (!hash) return "home";          // デフォルトは home にする

  return hash.replace("#", "");      // "#home" → "home"
}
JavaScript

ここでやっていることはシンプルです。

URL の hash を読む。
なければ “home” を返す。
あれば “#” を取り除いてタブ名にする。

これで「状態としてのタブ名」が手に入ります。


状態に応じて表示を切り替える関数を作る

表示更新の関数

function renderTab(activeTabName) {
  const views = document.querySelectorAll(".view");
  views.forEach(view => {
    const name = view.dataset.view;
    if (name === activeTabName) {
      view.style.display = "block";
    } else {
      view.style.display = "none";
    }
  });

  const tabs = document.querySelectorAll(".tabs a");
  tabs.forEach(tab => {
    const name = tab.dataset.tab;
    if (name === activeTabName) {
      tab.classList.add("is-active");
    } else {
      tab.classList.remove("is-active");
    }
  });
}
JavaScript

ここで大事なのは、
「表示は状態に従うだけ」 にしていることです。

activeTabName という「状態」を受け取って、
画面とタブの見た目をそれに合わせているだけです。


タブクリックで「状態」と「URL」を更新する

タブにイベントを付ける

function setupTabClick() {
  const tabs = document.querySelectorAll(".tabs a");

  tabs.forEach(tab => {
    tab.addEventListener("click", event => {
      event.preventDefault(); // 通常のリンク遷移を止める

      const tabName = tab.dataset.tab;
      window.location.hash = tabName; // URL の hash を更新

      renderTab(tabName); // 状態に応じて表示を更新
    });
  });
}
JavaScript

ここでやっていることは 3 ステップです。

通常のリンク遷移を止める。
クリックされたタブの名前を取り出す。
hash を書き換え、同時に表示も更新する。

ポイントは、
「状態の変更 = hash の変更」
と決めていることです。


hashchange イベントで「戻る / 進む」に対応する

ブラウザの戻る / 進むで hash が変わる

ユーザーがブラウザの戻るボタンを押すと、
URL の hash が変わります。

このときに「表示も変える」必要があります。

hashchange イベントを使う

window.addEventListener("hashchange", () => {
  const tabName = getCurrentTabFromHash();
  renderTab(tabName);
});
JavaScript

これで、

タブクリック → hash を変える → renderTab
戻る / 進む → hash が変わる → hashchange → renderTab

という流れが完成します。


再読み込み時に「さっきのタブ」を復元する

ここまでの仕組みができていれば、
実は「復元」はほぼ勝手に実現できます。

ページを再読み込みしても、
URL の hash はそのまま残るからです。

初期化処理で hash を見る

function init() {
  setupTabClick();

  const initialTab = getCurrentTabFromHash();
  renderTab(initialTab);
}

document.addEventListener("DOMContentLoaded", init);
JavaScript

これで、

https://example.com/app.html#settings

という URL で開いた場合、
最初から「設定タブ」が表示されます。

また、
タブを切り替えたあとに再読み込みしても、
そのときの hash に応じて同じタブが復元されます。


1日目の全体の流れを言葉でなぞる

ここまでを、コードではなく「流れ」で整理してみます。

タブは <a href="#home"> のように hash を持っている。
画面は data-view="home" のように名前を持っている。

ページ読み込み時に:

今の URL の hash から「どのタブか」を決める。
そのタブ名をもとに renderTab で表示を切り替える。
タブにクリックイベントを付けて、クリックされたら hash を書き換える。

ユーザーがタブをクリックしたら:

通常のリンク遷移は止める。
クリックされたタブの名前を読む。
window.location.hash にその名前を書く。
renderTab で表示を切り替える。

ユーザーが戻る / 進むを押したら:

hashchange イベントが発火する。
今の hash からタブ名を読み取る。
renderTab で表示を切り替える。

これで、

タブ切替
履歴対応
再読み込み復元

の 3 つが、
「hash を状態として扱う」という一本の軸でつながります。


今日いちばん深く理解してほしいこと

1日目の本質は、これです。

状態(今どの画面か)を URL の hash として表現し、
表示はその状態に従って更新するだけにする

タブをクリックした瞬間に DOM を直接いじるのではなく、
一度「状態」に落としてから表示を変える。

この感覚がつくと、
タブだけでなく、
ページング、フィルタ、モーダル、
あらゆる UI を「状態と表示の分離」で考えられるようになります。

2日目では、このタブ切り替えを
「クラス化」して、
アプリのどこでも再利用できる形にしていきます。

タイトルとURLをコピーしました