HOME / BLOG
BLOG

z-indexが効かない原因7選

z-index: 9999を書いたのに前へ出ない。こうした問題は値が小さいからではなく、要素が比較されるスタッキングコンテキストや、重なりを作る条件を見落としていることが原因です。

この記事では、z-indexが効かない代表的な原因を7つに分け、DevToolsでの確認方法と修正例を解説します。

z-indexの基本

z-indexは、配置された要素とその子孫、またはFlex/Gridアイテムの重なり順を指定します。大きな値ほど常にページ全体の最前面へ出るわけではなく、同じスタッキングコンテキスト内で比較されます。

.box {
  position: relative;
  z-index: 2;
}

原因1:positionがstaticのまま

通常フローの要素では、position: staticのままz-indexを指定しても期待どおり動かないケースがあります。まず配置要素にします。なお、Flex/Gridアイテムはpositionがstaticでもz-indexが使えます。

/* 通常の要素 */
.target {
  position: relative;
  z-index: 2;
}

原因2:親が別のスタッキングコンテキストを作っている

最も多い原因です。子へ大きなz-indexを指定しても、親のスタッキングコンテキストから外へは出られません。外側では親同士のz-indexが先に比較されます。

.parent-a {
  position: relative;
  z-index: 1;
}
.parent-a .child {
  position: absolute;
  z-index: 9999;
}

.parent-b {
  position: relative;
  z-index: 2;
}

この場合、.parent-a .childは親Bより前へ出ません。親Aのz-indexを上げるか、重ねたい要素を共通の親へ移動します。

原因3:transform・opacity・filterがコンテキストを作っている

transform、1未満のopacityfilterisolation: isolateなどは、新しいスタッキングコンテキストを作ることがあります。アニメーションやGPU対策で付けた指定が原因になることもあります。

.card {
  transform: translateZ(0); /* 新しいスタッキングコンテキスト */
}

.overlay {
  position: absolute;
  z-index: 9999;
}

不要なtransform等を外すか、重なり順をスタッキングコンテキストの親側で設計します。

原因4:兄弟ではなく、別階層の要素を比較している

z-indexはDOM全体で単純に並び替える値ではありません。別々の親に属する要素は、まず親の重なり順でグループとして比較されます。モーダルを深いコンポーネント内へ置くと起きやすいため、ページ直下の専用領域へ描画する方法が有効です。

<body>
  <main>...</main>
  <div id="modal-root"></div>
</body>

原因5:負のz-indexで親の背景へ潜っている

z-index: -1は、要素を単に一段後ろへ置く指定ではありません。親の背景や別コンテキストの背後へ入り、見えなくなる場合があります。

.decoration {
  position: absolute;
  z-index: -1;
}

.card {
  position: relative;
  isolation: isolate;
}

装飾をカード内の最背面へ置くなら、親へisolation: isolateを指定して範囲を閉じる方法があります。

原因6:overflowで切り取られている

z-indexが正しくても、祖先にoverflow: hiddenclipがあると、親の境界外へ出た部分は見えません。これは重なり順ではなく切り取りの問題です。

.card {
  overflow: hidden;
}

.dropdown {
  position: absolute;
  z-index: 100;
  /* cardの外側は切り取られる */
}

ドロップダウンやツールチップを外へ出すなら、overflowを外すか、切り取られない上位階層へ配置します。

原因7:top layerの要素と競合している

<dialog>のモーダル表示やPopover APIなど、top layerへ置かれた要素は通常のスタッキングコンテキストより上に表示されます。通常要素へ巨大なz-indexを付けても前へ出せません。

const dialog = document.querySelector('dialog');
dialog.showModal();

top layer同士の構造や開閉順を整理し、通常要素とのz-index競争にしないことが重要です。

CodePen

デバッグの順番

  1. 対象要素がpositioned要素またはFlex/Gridアイテムか
  2. 対象と競合要素が同じスタッキングコンテキストにいるか
  3. 祖先にtransform、opacity、filter、isolation等がないか
  4. 祖先のoverflowで切り取られていないか
  5. 競合相手がdialogやpopoverなどtop layerにいないか
  6. 値を999999へ増やす前に、親の重なり順を比較したか

まとめ

z-indexが効かない時は、数値ではなく「どのスタッキングコンテキスト内で比較されているか」を確認します。position、親のz-index、transform等、overflow、top layerの順で調べると原因を切り分けやすくなります。

参考資料