原文: 4 reasons your z-index isn’t working (and how to fix it)

z-index は、要素をレイヤー (層) 状に重ねて配置するために使う CSS プロパティです。とても便利かつ、CSS を扱うならば使い方を知っておくことが非常に重要なツールです。

残念ながら、z-index は動作が直観的でないことも多いプロパティです。最初の内は z-index の値が大きい要素が小さい要素の上に重なるというシンプルな動作に見えますが、動作を複雑にする追加のルールがあります。それで問題が起きるたび z-index を 999999 に設定して修正するわけにもいきませんよね。

この記事では z-index が動作しない原因として考えられる主な 4 つの理由と、その修正方法を解説します。

実際のコード例と問題の修正手順を見ていきます。この記事を読めば、z-index のよくある落とし穴を理解して回避できるようになるでしょう。

この記事の動画バージョンは筆者の YouTube チャンネル「Coder Coder」にて公開されています。

では最初の理由を見ていきましょう。

1. 同じ重ね合わせコンテキストの要素は、コードに書かれている順で前の要素の上に重ねて表示される

最初の例は、3 つの要素からなる比較的シンプルなレイアウトです。

  • 猫の画像
  • テキストが書かれた白いブロック
  • 同じ猫の画像がもう一枚

HTML のコードは次の通りです。

<div class="cat-top"></div> 

<div class="content__block"> Meow meow meow... </div> 

<div class="cat-bottom"></div>

このレイアウトで、テキストが書かれた白いブロックを 2 匹の猫の上に重ねて表示したいとします。

そのためにまず、猫の画像両方に負の数のマージンを設定して、白いブロックと一部分が重なるようにしました。

.cat-top { 
   margin-bottom: -110px; 
} 

.cat-bottom { 
   float: right; 
   margin-top: -120px; 
}

ところが今はこのような表示になっています。

See the Pen Z-index: #1: set position, #2: natural stacking order, #3: CSS properties by Jessica (@thecodercoder) on CodePen.

1 匹目の猫は、期待したとおり白いブロックの奥に表示されています。ですが 2 匹目の猫が手前に表示されてしまっています。

これはなぜでしょう。

このような表示になるのはウェブページの通常の重ね合わせ順が原因です。この基準が、どの要素が上にくるか下にくるかの基本的な順序を決めています。

要素に z-index が指定されていない場合でも、どちらが上に重なるかには理由があるのです。

この例ではどの要素にも z-index の値が設定されていません。したがって、重ね合わせ順はコード内の登場順で決まります。このルールでは、マークアップで後に登場する要素が前の要素の上 (手前) に重ねられます。

(重ね合わせ順序の基準については Mozilla Developer Network のページでより詳しく説明されています。)

この例では、猫の画像と白いブロックはこのルールに従って配置されています。1 匹目の猫が白いブロックの下 (奥) に、その白いブロックが 2 匹目の猫の下に入っているのはそのためです。

これで重ね合わせ順序については分かりました。では 2 匹目の猫が白いブロックの下にくるようにするには CSS をどう直したら良いのでしょう。

ここで 2 つ目の理由の登場です。

2. 要素に position プロパティが指定されていない

重ね合わせ順序を決めるもう 1 つの基準は、要素の position (位置) が指定されているかどうかです。

要素の位置を指定するには、CSS に position プロパティを追加して static 以外の値に設定します。例えば relativeabsolute などです。(この点について詳しくは、筆者によるこちらの記事 (英語) を参考にしてください。)

このルールでは、位置指定された (positioned) 要素は、位置指定されていない要素よりも手前に表示されます。

つまり白いブロックを position: relative に設定して、2 匹の猫の要素は位置指定されていない状態のままにすれば、白いブロックが猫の要素よりも手前に重ねられます。

表示は次のようになります。先ほどの Codepen を使って動作を確認しても構いません。

NlDhNhhBXrXmR35aZCt4XCGbfBCt4bRPLHzK

次は transform プロパティを使って、下側の猫を上下逆になるように回転させようと思います。そうすると両方の猫が白いブロックより奥に表示され、顔だけを出した状態になるはずです。

ですが上記の設定は、さらなる z-index 関連の混乱を招きます。次のパートで問題とその解決方法を説明します。

3. opacity や transform など一部の CSS プロパティが設定された要素は、新しい重ね合わせコンテキストに移動される

先ほど述べたように、下側の猫を上下逆に表示したいと思います。そのために transform: rotate(180deg) を追加します。

.cat-bottom { 
   float: right; 
   margin-top: -120px; 
   transform: rotate(180deg); 
}

しかしこの設定を適用すると、下側にいる猫はまた白いブロックの手前に表示されるようになってしまいます。

GTJjexmzYUkJa37hDuIUALqGngUjSl0wxFcE-1

一体何が起きているのでしょうか?

この問題に遭遇することは少ないかもしれませんが、これも重ね合わせ順序の特徴の一つです。transformopacity など一部の CSS プロパティは、要素を専用の新しい重ね合わせコンテキスト (stacking context) に移動します。

どういうことかと言うと、.cat-bottom の要素に transform を追加すると、この要素は z-index が 0 の時と同じ状態になります。positionz-index も設定していないのにです。(この挙動を opacity プロパティの場合について説明している W3.org のドキュメントがあります。)

白いブロックに追加したのは position: relative だけで、z-index の値は追加していないことを思い出してください。それだけで位置指定されていない猫の要素より手前に配置されるようにできていました。

しかし transform の設定によって、.cat-bottom の要素が z-index: 0 で相対位置指定された場合と同じようにふるまうようになり、白いブロックの手前に表示されるようになってしまいました。

この場合の修正方法は、せめて白いブロックの方だけでも position: relativez-index を明示的に設定することです。念のため猫の要素にも position: relative とさらに小さい z-index を設定しても構いません。

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

.cat-top, .cat-bottom { 
   position: relative; z-index: 1; 
}

これで基本的な z-index の問題のほとんどは解決できると思います。

では z-index が動作しない最後の理由に移りましょう。今度は親要素と子要素が絡んでくるので少し複雑です。

4. 親要素の z-index レベルが原因で、要素が低い重ね合わせコンテキストにある

このケースのコード例を見てみましょう。

See the Pen Z-index: #4 different stacking contexts by Jessica (@thecodercoder) on CodePen.

シンプルなウェブページの例です。よくあるコンテンツと、その手前に「Send Feedback」と書かれたピンクのサイドタブが配置されています。

そして猫の写真をクリックするとモーダルウィンドウが開き、背景に半透明のグレーがオーバーレイ表示されます。

しかしモーダルウィンドウが開いた時、サイドタブがグレーのオーバーレイより手前に表示されています。このオーバーレイを、サイドタブも含めたすべての表示内容を覆うような形で手前に表示させたいと思います。

問題となっている要素の CSS を見てみましょう。

.content { 
   position: relative; 
   z-index: 1; 
} 

.modal { 
   position: fixed; 
   z-index: 100; 
} 

.side-tab { 
   position: fixed; 
   z-index: 5; 
}

すべての要素が位置指定されています。そしてサイドタブの z-index が 5 に設定されているため、z-index: 1 となっているコンテンツの要素より手前に表示されます。

次にモーダルには z-index: 100 が設定されているので、z-index: 5 のサイドタブより手前に表示されるはずです。しかし実際にはモーダルがサイドタブの奥に表示されてしまっています。

どうしてこうなっているのでしょう?

ここまでに、重ね合わせコンテキストに影響する要因をいくつか見てきました。position が指定されているかどうかや、マークアップ内での登場順などです。

ここでさらにもう一つ、重ね合わせコンテキストの特徴を紹介します。子要素はその親要素の重ね合わせコンテキスト内に制限されるということです。

問題となっている 3 つの要素を詳しく見てみましょう。

マークアップは次のようになっています。

<section class="content">            
    <div class="modal"></div>
</section>

<div class="side-tab"></div>

このマークアップを見ると、コンテンツとサイドタブの要素は兄弟要素であることがわかります。つまり、この 2 つの要素はマークアップの中で同じ階層に位置します。(この階層は z-index の階層とは異なります。) そしてモーダルは、コンテンツの要素の子要素となっています。

モーダルがコンテンツの要素の内側にあるので、z-index を 100 に設定しても、その親要素内でしか効果を発揮できません。例えばもしモーダルから見て兄弟要素にあたる他の子要素があったとしたら、その兄弟要素はお互いの z-index の値に応じて手前に表示されたり奥に表示されたりします。

しかしその z-index の値は、親要素の外側には影響しません。親要素であるコンテンツの要素は  z-index が 1 に設定されているためです。

そのため子要素 (モーダルも含む) は、この z-index レベルの外に出ることができません。

(少し気が滅入るようなメタファーですが「子は親に制限されていてそこから出ることができない」と憶えてもいいでしょう。)

この問題を解決する方法は 2 つあります。

解決法 1: モーダルを親であるコンテンツ要素の外に出し、ページのメインの重ね合わせコンテキストに移動させる

この方法で修正したマークアップがこちらです。

<section class="content"></section>

<div class="modal"></div>

<div class="side-tab"></div>

これで、モーダルの要素が他 2 つの要素と兄弟要素になりました。これで 3 つの要素すべてが同じ重ね合わせコンテキストとなり、お互いの z-index レベルに応じて重ねられます。

この新しい重ね合わせコンテキストでは、要素は次の順序で上 (手前) から順に重ねられます。

  • モーダル (z-index: 100)
  • サイドタブ (z-index: 5)
  • コンテンツ (z-index: 1)

解決法 2: コンテンツから位置指定を削除して、モーダルの z-index を制限しないようにする

マークアップを変更したくない、あるいは変更できない場合、コンテンツの要素の position 指定を削除することで問題を修正できます。

.content { 
   // No position set 
} 

.modal { 
   position: absolute; 
   z-index: 100; 
} 

.side-tab { 
   position: absolute; 
   z-index: 5; 
}

コンテンツの要素が位置指定されていない状態になったので、モーダルの z-index の値を制限しなくなりました。これで、より高い z-index の値 100 を持つモーダルがサイドタブの要素よりも手前に表示されます。

この方法でも修正できますが、筆者は個人的には 1 つめの方法を使います。

なぜかと言うと、将来的に何らかの理由があってコンテンツの要素を位置指定しなければならなくなったときに、またモーダルの重ね合わせ順が制限されてしまうためです。

まとめ

この記事がお役に立てば幸いです!まとめると、z-index の問題はたいてい次のガイドラインに従って解決できます。

  1. 要素が位置指定されているか、z-index の値が正しい順序になっているか確認する
  2. 親要素によって子要素の z-index レベルが制限されていないか確認する

もっと読む

筆者のブログ coder-coder.com でもウェブ開発に関するチュートリアルを執筆しています。また Instagram にもちょっとしたコツを投稿しています。