z-index要素の重なりを管理する方法(スタッキングコンテキスト化で解決)

z-index要素の重なりを管理する方法(スタッキングコンテキスト化で解決)

z-index要素の重なりを管理する方法(スタッキングコンテキスト化で解決)

z-indexは要素の重なり具合を調整するcssプロパティです。

z-indexを効かせるにはposition: static以外の値を入れておく必要があります。

重なり順はz-indexの数値の大小ではなく、親要素などが持つスタッキングコンテキストで管理します。

z-indexの重なりを管理するポイント

・基本的には要素を全てスタックレベルで分割します。
・要素内の子要素がz-indexの値を必要としない場合は分割する必要はないです。
・要素にz-indexの値を入れる場合、10単位で入れるか変数を使用しましょう。
・全ては要素のスタックレベルとz-indexの値で決まります。
・兄弟となる要素がある場合、その両方にz-indexを割り当てましょう。
・子要素自体がスタッキングコンテキストを持ってしまうと上手く並びません。
・デバッグは正しく重なっていない2つの要素の最初の親要素を見つけて、その要素内のz-indexを適宜変更するだけです。

z-indexとは?

z-indexは要素の重なり順を指定するCSSのプロパティです。

要素が複数重なっている時、どれを手前に(上に)どれを後ろに(下に)するか、その順序を指定するプロパティですね。

WEBページを3次元として考える立体要素になります。

基本的には、後から記述されたものが画面では手前(上)に配置されますが、z-indexにより自由に重なりの順番を設定する事ができます。

z-indexの書き方

z-index設定ルール

positionプロパティで「static」(初期値)以外の値を適用

position: relativeやabsoluteなどが必要
「auto」を入力した場合は親要素と同じ値が設定されます。

z-index: 数値;を入れる

値は整数で入力し、大きい数値のものほど手前に表示されます。

値が同じ場合はソース記述の順に

z-indexの値が同じ場合は、後からhtmlに記述された方が後から描画されます。

親要素でz-indexの指定がされていない場合、初期値は0となります。

基本的にz-indexの数値は10単位で書く

後から間に入れなければならない要素などが出現した場合に10単位であればその間の数値を取る事ができるためですね。

1,2,3と連番につけているといざという時に間の数値を取る事ができません。できれば10,20,30とつけましょう。

※小数点の数値を付ける事ができないためです。

z-index記述例

HTMLソース例

<div class="z_wrap"> <div class="div1"> <p>div1</p> </div> <div class="div2"> <strong>div2</strong> </div> <div class="div3"> <p>div3</p> </div> </div>

CSSによる>z-indexの記述

.z_wrap { height: 400px; position: relative; } .div1, .div2, .div3 { padding: 5px; width: 500px; height: 200px; border: solid 1px #666; position: absolute; } .div1 { left: 30px; top: 10px; background-color: rgba(220, 50, 170, 0.9); } .div2 { left: 60px; top: 90px; background-color: rgba(36, 170, 220, 0.9); z-index: 1; } .div3 { left: 90px; top: 170px; background-color: rgba(170, 220, 36, 0.9); z-index: 2; }

z-index通常の重なり結果

z-index通常の重なり

上記の実行結果は画像にしています。

z-indexが効かないときの対処法

陥りやすいミスとして繰り返しますが、要素のpositionが「static」のままではz-indexが効きません。

要素のpositionを何も設定していない場合、その要素はposition: staticの状態になっています。

ですので、positionを「relative」や「absolute」など「static」以外の値にする必要があります。

z-indexが効かない場合は、まず要素のpositionを確認してみましょう。

スタッキングコンテキストの概念

z-indexは本来2次元のWEBページに3次元の要素を設定してその重なり順を定義するプロパティです。

値が大きいほど要素は上に重なりますので、描画の順序を操作するプロパティとも言えますね。

しかしz-indexはただ数値が高ければそれだけ上に来るものではありません。

実際z-indexの数値を9999にしているのに、上に来ない事ってありますよね?

これは親要素の持つ影響が原因です、これが「スタッキングコンテキスト」です。

ここでz-indexの重なりに絶対的な影響力を持つ、スタッキングコンテキスト(スタック文脈・スタックレベル)と呼ばれる概念について解説をしておきましょう。

5つの要素を例に取ったz-indexの重なり

例として、div1,div2,div3の3つの外枠の要素があり、さらにdiv1の入れ子でdiv1-1、div2の入れ子でdiv2-1が存在しているとします。

この合計5つの要素の重なりを見てみましょう。

HTML

<div class="div1"> <p>div1</p> <div class="div1-1"> <p>入れ子div1-1</p> </div> </div> <div class="div2"> <strong>div2</strong> <div class="div2-1"> <p>入れ子div2-1</p> </div> </div> <div class="div3"> <p>div3</p> </div>

CSS

.z_wrap { height: 400px; position: relative; } .div1, .div1-1, .div2, .div2-1, .div3 { padding: 5px; width: 500px; height: 200px; border: solid 1px #666; position: absolute; } .div1 { left: 30px; top: 10px; background-color: rgba(220, 220, 170, 0.9); } .div2 { left: 60px; top: 90px; background-color: rgba(220, 170, 220, 0.9); z-index: 1; } .div3 { left: 90px; top: 170px; background-color: rgba(170, 220, 220, 0.9); z-index: 2; } .div1-1, .div2-1 { width: 200px; height: 150px; } .div1-1 { left: 270px; top: 30px; color: #fff; background-color: rgba(42, 170, 17, 0.9); z-index: 10; } .div2-1 { left: 80px; top: 30px; color: #fff; background-color: rgba(17, 220, 170, 0.9); z-index: 10; }

入れ子の実行例

入れ子の実行例

上記の実行結果は画像にしています。

記述上の順序とスタックレベル上の順序

仮にこのスタッキングコンテキストの概念を無視すると、z-indexの数値順ですから順序は次のようになるはずです。

div1(z-indexなし)
div2(z-indexが1)
div3(z-indexが2)
div1-1(z-indexが10)
div2-1(z-indexが10)

div1-1とdiv2-1はz-indexが同じ「10」の値になっていますね。

この場合、html上の記述ではdiv2-1の方がdiv1-1より後から書かれているので上に重なると思うかもしれません。

でも実行例ではそのようになっていません。div2-1の上にdiv3が重なっていますよね。

div2-1はz-indexが10であるにもかかわらずです。

なぜこのような事が起こるのでしょうか。それは「スタッキングコンテキスト」が大きく影響している事が理由です。

特定条件下でその要素は「スタッキングコンテキスト」を持つ

親要素と子要素だけの世界(スタックレベル)になる

ズバリ結論を申し上げておきます。

親要素がスタッキングコンテキストを持つと、その「子」となる要素にどんなz-index値が掛かれていても、外の世界に出さない様に親要素が包み込んでしまうのです。

ですので、その親要素以外の数値の影響を受ける事はありません。

スタッキングコンテキストを持つ要素が描画されると、全ての子要素は「親要素に対しての重なり」のみに集中するのです。

スタックレベルを含んだ構成順

つまり実際の描画する順序は次の通りです。

div1
div2(div2-1)
div3
div1-1

div2には「z-index: 1」が設定されているので、ここでdiv2に「スタッキングコンテキスト」が発生します。

つまりdiv2の世界の中だけで、div2-1がどうあるべきかを問われている事になります。

z-indexは10なのでdiv2より上に重なっていますが、div2の世界の外である「div3」より上に来る事は無く、下に描画されます。

もちろんdiv3もz-index:2ですので、div3の世界だけのスタックコンテキストを持ちます。

div1-1は何故一番上に来ているのか

よく見るとdiv1-1はdiv1の子要素で上の方に記述されているのに、実行例では一番上に重なっています。

これは何故でしょう?

div1はスタッキングコンテキストを持たない

上のCSSを見てもわかる通り、親要素となるdiv1にz-indexの値はありません(z-index:autoの状態)。

この状態はスタッキングコンテキストの世界を持っていない事を指しています。

そのため、div1-1(z-index: 10)は、div1の入れ子で前半に書いてあるにも関わらず、後に描かれているdiv2(z-index: 1)やdiv3(z-index: 2)よりも上に来るのです。

スタッキングコンテキストを持つ条件

親要素が以下の条件を持つ場合にスタッキングコンテキストを持つようになります。

・position値が「absolute」「relative」で、z-indexの値が「auto」以外
・position値が「fixed」か「sticky」の場合
・display値が「flex」か「grid」の子要素で、かつz-index値が「auto」以外
・opacity値が「1」以外
・mix-blend-mode値が「normal」以外
・transform、filter、perspective、clip-pathの値が「none」以外
・will-change値が「auto」以外、etc

大規模サイトにおけるz-indexの管理について

上の様に一か所のhtmlとcssだけならスタッキングコンテキストの状態を確認する事は容易ですよね。

しかし大規模なWEBサイトになると、WEBページ自体が数十および数百の要素(コンポーネント)で構成される事も少なくありません。

そしてそのそれぞれに定義されたz-indexの記述がある場合は面倒な事になります。

z-index値のあるプロパティを集約するべき

css管理の基本として、全てのz-index値の記述は1か所にグループ化する事が提唱されています。

しかしそれだとその要素が同じスタックレベル内に属していないと、z-index値だけでは比較できません。

大規模なアプリケーションでは簡単にcss管理(グループ化)がしにくい面があるのですね。

重なりを修正するためにどうするのがベストか

例を挙げながら重なりを修正するベストプラクティスを見ていきましょう。

以下のようなWEBページがあるとします。

HTML

<div class="header"> Header <div class="overlay"> Overlay </div> </div> <div class="main"> Main Content </div>

CSS

.header { background-color: rgba(255, 255, 220, 0.8); position: relative; width: 100%; height: 100px; } .overlay { position: absolute; z-index: 10; background-color: rgba(255, 220, 255, 0.8); left: 200px; top: 25px; width: 300px; height: 100px; filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.3)); } .overlay::after { content: ""; position: absolute; left: 50%; transform: translateX(-50%); bottom: 100%; width: 0; height: 0; border-top: 10px solid transparent; border-right: 10px solid transparent; border-left: 10px solid transparent; border-bottom: 10px solid rgba(255, 220, 255, 0.8); } .main { background-color: rgba(220, 255, 255, 0.8); height: 100px; position: relative; z-index: 1; }

実行結果

ヘッダーのドロップダウンメニュー

ヘッダーにドロップダウンメニューを作成する場合、上記のような状態の実現が必要ですよね。

上記の例でいうと「overlay」の部分がドロップダウンメニューです。

もちろん要素の上に重ねる必要がありますので、overlayにz-index: 10を設定しています。

CSS内を更新

ではこの状態でヘッダーCSSに、例えばtranslateZなどのハックを記述したとします。

css

.header { background-color: rgba(255, 255, 220, 0.8); width: 100%; height: 100px; transform: translateZ(0); }

実行結果

ドロップダウンメニュー部分が埋もれる

ご覧の通り、Main Contentの下にドロップダウンであるOverlay部分が潜り込む様になりました。

その理由はヘッダーがスタッキングコンテキストを持つ様になったためです。

このようにtransformの値が「none」以外のプロパティを持ったり、その要素自体がz-index:が「auto」以外になるとスタッキングコンテキストを持つのです。

この場合、以下の様にz-index値を設定する事で解決します。

修正CSS

.header { background-color: rgba(255, 255, 220, 0.8); width: 100%; height: 100px; transform: translateZ(0); z-index: 2; }

実行結果

埋もれが改善した状態

きちんとMain contentの上に重なる様になりましたね。

要素がそれぞれ個別のz-index値を持つ場合

問題なのは、要素の中に子要素がありさらに別の子要素も存在し、それぞれが異なるz-indexを持つ場合です。

この時どのようなルールでレイアウト維持をするべきなのでしょうか。

上記の様に安易にheaderのz-indexを変更して、レイアウトが壊れない保証はありませんよね。

明確な基本ルール

明確な一つのルールを押さえておきましょう。非常に簡単です。

要素がスタッキングコンテキストを持つと、影響するのはその要素内のみであり、それ以外には影響しません。

別の言い方をすれば、CSSファイルの特定要素のz-index値を変更する場合、「親要素」や「兄弟要素」にのみに注意しておけばいい事になります。

それ以外のところにどんなz-index値が書いてあっても無視して良いのです。

全てのz-indexの値ではなく、親要素内の値だけを見る

繰り返しますがz-indexなどで要素がスタッキングコンテキストを持つと、その要素の外には影響を及ぼさないというルールがあります。

つまり、全ての要素がスタッキングコンテキストを持っていれば、各要素のz-indexを変更する時は親要素や兄弟要素を見るだけでOKと言えますね。

管理効率の良いレイアウト構成を工夫する

次に管理効率が良いレイアウトを構成する1つの方法をご紹介します。

前回より多くの要素を記述しますが、開発者の時間を節約する事ができます。

レイアウト構成

HTML

<div class="wrap_container"> <div class="wrap_header"> <div class="header1"> Header <div class="overlay1"> Overlay </div> </div> </div> <div class="wrap_main"> <div class="main1"> Main Content </div> </div> </div>

CSS

/* wrap styles */ .wrap_container { position: relative; z-index: 0; } .wrap_header { position: relative; z-index: 2; } .wrap_main { position: relative; z-index: 1; } /* Header styles */ .header1 { background-color: rgba(255, 255, 220, 0.9); width: 100%; height: 100px; transform: translateZ(0); position: relative; } .overlay1 { position: absolute; background-color: rgba(255, 220, 255, 0.9); left: 200px; top: 35px; width: 300px; height: 100px; filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.5)); } .overlay1::after { content: ""; position: absolute; left: 50%; transform: translateX(-50%); bottom: 100%; width: 0; height: 0; border-top: 10px solid transparent; border-right: 10px solid transparent; border-left: 10px solid transparent; border-bottom: 10px solid rgba(255, 220, 255, 0.9); } /* Main styles */ .main1 { background-color: rgba(220, 255, 255, 0.9); width: 100%; height: 100px; position: relative; }

実行例

先ほどと同じ状態
先ほどと同じ状態のレイアウトです。

上記コードの解説

スタックさせるベース要素を用意する

一つ前のコードと違い、headerやmainをそれぞれwrap_headerやwra_mainで囲っています。

wrap_headerはz-index: 2を保持し、wrap_mainはz-index: 1を保持しています。

これによりwrap_headerやwrap_mainは「兄弟の要素」としてスタック化されています。

スタッキングコンテキスト内での状態だけを見る

それでもまだ、header1とmain1はposition: relativeであり、かつz-index: 0の状態です。

header1を見るとOverlay1が配置されているだけですね。上に重ねるのはこれだけです。

この時header1やmain1は、wrap_headerやwrap_mainともにセットでスタックレベル化にある訳ですから、それ以外の世界とは関係がありません。

ですのでheader1とOverlay1の両方からz-indexプロパティを削除できます。

同様にmain1もz-index記述の必要はありません。

ここが今回のアプローチの最大の利点です。

下に敷かれる予定の兄弟要素を全てスタック化させれば、中の要素が外の要素に影響を与えなくなるので、管理がしやすくなります。

/* Root styles */ .wrap_container { position: relative; z-index: 0; } .wrap_header { position: relative; z-index: 2; } .wrap_main { position: relative; z-index: 1; } /* Header styles */ .header1 { background-color: rgba(255, 255, 220, 0.9); width: 100%; height: 100px; transform: translateZ(0); position: relative; z-index: 0; 消せる } .overlay1 { position: absolute; z-index: 1; 消せる background-color: rgba(255, 220, 255, 0.9); left: 200px; top: 35px; width: 300px; height: 100px; filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.5)); } .overlay1::after { content: ""; position: absolute; left: 50%; transform: translateX(-50%); bottom: 100%; width: 0; height: 0; border-top: 10px solid transparent; border-right: 10px solid transparent; border-left: 10px solid transparent; border-bottom: 10px solid rgba(255, 220, 255, 0.9); } /* Main styles */ .main1 { background-color: rgba(220, 255, 255, 0.9); width: 100%; height: 100px; position: relative; z-index: 0; 消せる }

まとめ

複雑に絡む各要素のz-indexを管理するためのポイントとまとめました。

・基本的には要素を全てスタックレベルで分割します。
・要素内の子要素がz-indexの値を必要としない場合は分割する必要はないです。
・要素にz-indexの値を入れる場合、10単位で入れるか変数を使用しましょう。
・全ては要素のスタックレベルとz-indexの値で決まります。
・兄弟となる要素がある場合、その両方にz-indexを割り当てましょう。
・子要素自体がスタッキングコンテキストを持ってしまうと上手く並びません。
・デバッグは正しく重なっていない2つの要素の最初の親要素を見つけて、その要素内のz-indexを適宜変更するだけです。

z-indexを使う時はpositionプロパティを必ず記述し、値に「static」以外の値を入れる事に注意しよう。

この記事をシェアする

一押し人気コーナー紹介

HTMLカテゴリの関連記事