JavaScript | DOM 操作:CSS / クラス操作 – display の切り替え

JavaScript
スポンサーリンク

display の切り替えとは何か

要素の「表示・非表示」を制御する最基本の方法が display の切り替えです。display: none にすると要素は完全に消え、ページのレイアウト計算からも外れます。再表示するときは本来の display(block・inline・flex・grid など)を戻します。ここが重要です:消す方式と戻し方を正しく選ばないと、レイアウト崩れやアクセシビリティの問題が起きます。


最小構成の切り替え方法(クラス方式とインライン方式)

クラス方式(推奨:保守性が高い)

<style>
  .is-hidden { display: none !important; }
</style>

<button id="toggle">切り替え</button>
<div id="panel" class="panel">内容</div>

<script>
  const btn = document.getElementById("toggle");
  const panel = document.getElementById("panel");

  btn.addEventListener("click", () => {
    const hidden = panel.classList.toggle("is-hidden");
    btn.textContent = hidden ? "開く" : "閉じる";
  });
</script>
HTML

ここが重要です:見た目のルールは CSS に置き、JS は“状態クラス”だけ触る。ルールが一箇所に集約され、チーム開発で壊れにくくなります。

インライン方式(最短だが設計が分散しやすい)

<button id="toggle">切り替え</button>
<div id="panel" style="display: block;">内容</div>

<script>
  const btn = document.getElementById("toggle");
  const panel = document.getElementById("panel");

  btn.addEventListener("click", () => {
    const isHidden = panel.style.display === "none";
    panel.style.display = isHidden ? "block" : "none";
    btn.textContent = isHidden ? "閉じる" : "開く";
  });
</script>
HTML

ここが重要です:その場の小さな UI には便利ですが、画面全体では「どこが display を決めているのか」が分散します。基本はクラス方式、インラインは例外的に。


深掘り:元の display を覚えて正しく戻す

block か inline か flex か——戻し先を間違えない

要素の本来の表示モードはさまざまです。毎回 “block” に戻すと、inline 要素や flex コンテナが壊れます。安全に扱うには「初期表示値」を覚えておきます。

<div id="toolbar" class="toolbar">…</div>
<script>
  const el = document.getElementById("toolbar");
  // 初回に本来の display を記録(空文字なら CSS が決める)
  const original = getComputedStyle(el).display;
  el.dataset.originalDisplay = original === "none" ? "" : original; // “none”なら空にし CSS に委ねる

  function show(el) {
    el.style.display = el.dataset.originalDisplay || "";
  }
  function hide(el) {
    el.style.display = "none";
  }
</script>
HTML

ここが重要です:display の“正解”は要素次第。getComputedStyle で一度だけ取り、data-* に記録して使い回すと安全です。


表示の種類とアクセシビリティ(見え方だけでなく意味も)

display: none と visibility: hidden の違い

  • display: none
    • 完全に消える。レイアウトから除外。スクリーンリーダーにも読まれないことが多い。
  • visibility: hidden
    • その場所は確保されるが見えない。スクリーンリーダーには読まれることがある。

ここが重要です:操作不能にしたい非表示は display: none。見えないが領域を保持したいなら visibility: hidden。意味が違います。

スクリーンリーダーとフォーカス管理

「見えない=操作不可」に揃えるため、非表示時はフォーカスも外します。

function hideAccessibly(el) {
  el.classList.add("is-hidden");             // 視覚的に隠す
  el.setAttribute("aria-hidden", "true");    // 読み上げ対象から外す
  el.inert = true;                           // クリック・フォーカスを無効(対応ブラウザ)
}

function showAccessibly(el) {
  el.classList.remove("is-hidden");
  el.setAttribute("aria-hidden", "false");
  el.inert = false;
}
JavaScript

ここが重要です:視覚・読み上げ・フォーカスの整合性を取る。特にモーダルやメニューは“見えないのに tab で入れる”事故を防ぐ必要があります。


アニメーションする場合の考え方(display は直接は動かない)

display はトランジション不可。ラッパーで“擬似的に”見せる

<style>
  .drawer { overflow: hidden; max-height: 0; transition: max-height 200ms ease; }
  .drawer.is-open { max-height: 300px; } /* 実際の高さに合わせて十分な値に */
</style>

<button id="toggle">開く</button>
<div id="drawer" class="drawer">…内容…</div>

<script>
  const drawer = document.getElementById("drawer");
  document.getElementById("toggle").addEventListener("click", () => {
    const open = drawer.classList.toggle("is-open");
    // 開閉の文言同期
  });
</script>
HTML

ここが重要です:display はアニメーションできません。高さ・不透明度・transform を使うのが定石。開閉はクラス切り替え、動きは CSS に任せます。

フェードの場合の設計

<style>
  .fade { opacity: 0; pointer-events: none; transition: opacity 180ms; }
  .fade.is-show { opacity: 1; pointer-events: auto; }
</style>
HTML

ここが重要です:opacity と pointer-events を組み合わせると“見えないが操作も不可”が実現できます。読み上げの制御が必要なら aria-hidden も併用します。


代表パターンの実装例(ドロップダウン・モーダル)

ドロップダウンメニュー

<button id="open" aria-expanded="false">メニュー</button>
<ul id="menu" class="menu is-hidden" role="menu">
  <li role="menuitem">A</li>
  <li role="menuitem">B</li>
</ul>

<style>
  .is-hidden { display: none !important; }
</style>

<script>
  const btn = document.getElementById("open");
  const menu = document.getElementById("menu");

  btn.addEventListener("click", () => {
    const hidden = menu.classList.toggle("is-hidden");
    btn.setAttribute("aria-expanded", String(!hidden));
  });

  // メニュー外クリックで閉じる
  document.addEventListener("click", (e) => {
    if (!(e.target instanceof Element)) return;
    if (!menu.contains(e.target) && e.target !== btn) {
      menu.classList.add("is-hidden");
      btn.setAttribute("aria-expanded", "false");
    }
  });
</script>
HTML

ここが重要です:表示状態(is-hidden)を“単一情報源”にし、aria-expanded を同期させる。外側クリックで閉じるのは UX の基本。

モーダル(背景ロックとフォーカス制御)

<button id="show">開く</button>
<div id="backdrop" class="backdrop is-hidden"></div>
<div id="modal" class="modal is-hidden" role="dialog" aria-modal="true">
  <button id="close">閉じる</button>
</div>

<style>
  .is-hidden { display: none !important; }
  .backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.4); }
  .modal { position: fixed; inset: 20% auto auto 20%; background: #fff; padding: 16px; }
  .lock { overflow: hidden; } /* body に付ける */
</style>

<script>
  const body = document.body;
  const modal = document.getElementById("modal");
  const backdrop = document.getElementById("backdrop");
  const showBtn = document.getElementById("show");
  const closeBtn = document.getElementById("close");

  function openModal() {
    modal.classList.remove("is-hidden");
    backdrop.classList.remove("is-hidden");
    body.classList.add("lock");
    modal.setAttribute("aria-hidden", "false");
    modal.querySelector("button")?.focus();
  }

  function closeModal() {
    modal.classList.add("is-hidden");
    backdrop.classList.add("is-hidden");
    body.classList.remove("lock");
    modal.setAttribute("aria-hidden", "true");
    showBtn.focus();
  }

  showBtn.addEventListener("click", openModal);
  closeBtn.addEventListener("click", closeModal);
  backdrop.addEventListener("click", closeModal);
</script>
HTML

ここが重要です:背景スクロールのロック、初期フォーカス、閉じた後のフォーカス返却まで面倒を見る。アクセシビリティが揃うと“自然な”体験になります。


落とし穴と回避策(タイミング・競合・テスト)

DOM がないタイミングで触らない

DOMContentLoaded 後、または defer スクリプトで実行。常に null ガードを入れます。ここが重要です:初期化が安定します。

CSS と JS の契約を揃える

  • 非表示は is-hidden に統一(!important で強くしておく)
  • 状態クラスは is-、テーマは theme-、種類は type-* に分ける

ここが重要です:クラス命名の契約が整っていると、表示制御のコードが短く、読みやすくなります。

テスト観点

  • 画面上は隠れているか(視覚)
  • Tab でフォーカスできないか(操作)
  • スクリーンリーダーに読まれないか(aria/inert)
  • レイアウトに影響が出ていないか(display と visibility の違い)

ここが重要です:表示・操作・読み上げ・レイアウトの4視点で確認するのが“安全な切り替え”の基準です。


まとめ

display の切り替えは「表示・非表示」の最短手。基本はクラス方式で一元管理し、インラインは例外的に。元の display を記録して正しく戻し、アクセシビリティ(aria-hidden・inert・フォーカス管理)を揃える。アニメーションは display を直接動かさず、height・opacity・transform とクラス切り替えで実現。命名と契約を整え、4視点(視覚・操作・読み上げ・レイアウト)でテストすれば、初心者でも壊れにくい表示制御が書けます。

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