12周年記念パーティ開催! 2024/5/10(金) 19:00

z-index再入門 第1回 z-indexの仕組み

z-indexは要素の重なりを指定するプロパティですが、その仕組みを正確に押さえておかないと、思い通りの重なり順になりません。まずはz-indexの仕様も含め、z-index使用に必須項目を解説します。

発行

著者 坂巻 翔大郎 フロントエンド・エンジニア
z-index再入門 シリーズの記事一覧

はじめに

z-indexプロパティは要素の重なり順を指定することができます。z-indexプロパティを扱う際に、その原理やハマりどころを理解しておくと、より安全なCSSを書くことができるようになります。

z-indexを扱う上でのハマりどころ

z-indexとpositionプロパティを使用したレイアウトで、要素の重なり順序を指定した際に、意図した通りに配置されない、ということが過去にあった人は少なくないと思います。

意図した重なり

次のサンプルは、オレンジの親要素を上に、グリーンの子要素はその下に配置したいと考え、その通りの結果が得られたものです。

<div class="block">
  <div class="block__bg"></div>
</div>
.block {
  position: relative;
  width: 100px;
  height: 100px;
  background: tomato;
}
.block__bg {
  position: absolute;
  z-index: -1;
  top: 15px;
  left: 15px;
  width: 100px;
  height: 100px;
  background: green;
}

.block__bgのz-indexに-1を指定し、その親要素の.blockよりも下へ配置しました。

意図しない重なり

ですが、先ほどのソースコードの.blockのz-indexにauto以外の値(整数)を指定した場合は、下に配置しようとしていたはずのグリーンの子要素が、オレンジの親要素の上に配置されてしまいます。このサンプルでは、親要素の.blockz-index: 1;を指定しています。

コードは次のようになっています。

<div class="block">
  <div class="block__bg"></div>
</div>
.block {
  position: relative;
  z-index: 1; /* z-indexを書き加えた */
  width: 100px;
  height: 100px;
  background: tomato;
}
.block__bg {
  position: absolute;
  z-index: -1;
  top: 15px;
  left: 15px;
  width: 100px;
  height: 100px;
  background: green;
}

一見すると、子要素の.block__bgにはz-index: -1が指定されていて、親要素の.blockにはz-index: 1が指定されているので、親要素が前面にきそうな気がするのですが、そうはなりません。

なぜこのようなことが起こるのでしょう。

この仕組みを理解するためには、z-indexの仕組み、positionの仕組み、そしてスタック文脈の形成という3つのポイントを押さえる必要があります。それらを順番に解説していきます。

z-indexプロパティ

z-indexプロパティは、ボックスの重なり順序を指定する際に使用します。原則*としては、値が大きい要素ほど前面にきます。

*注:原則

ただしあくまでもこれは「原則」の話です。後述するスタック文脈によって、単純に大きな値が指定されている要素が前面にくるわけではありません。前掲のサンプルで見てもらった通りです。

z-indexプロパティは、positionプロパティの初期値(static)以外の値が指定されている要素には、自動的にautoが適用された状態になっています。

z-indexに指定できる値は以下の2種類が存在します。

説明
auto 初期値。親要素と同じ階層になる。
整数値 重なり順序を整数で指定する。0を基準とし、-2147483647から2147483647までの値を指定できる。

z-indexプロパティの最大値は、2147483647となっています。これは2147483647 = 2^31 – 1 = 0×7FFFFFFFで符号付き32bit整数の最大値です。

過去には、2147483647以下であるブラウザもありましたが、モダンブラウザでは2147483647が最大値となっています。

次に、z-indexプロパティともっとも関係するpositionプロパティの仕様を確認しましょう。

positionプロパティ

positionプロパティは、ボックスの基準位置を、相対位置か絶対位置なのかを指定する際に使用できます。要素の表示位置の指定には、top・bottom・left・rightプロパティを併用し、指定した基準位置からの距離を設定します。

positionプロパティに指定できる値は、5種類存在します。

説明
static 初期値。特に配置方法を指定しない。
relative 相対位置への配置となる。
absolute 絶対位置への配置になる。absoluteを指定した親ボックスにstatic以外の値が指定されている場合に、その親ボックスの左上が基準位置になる。親ボックスに指定がない場合は、body要素が起点となる。
fixed absoluteと同じく絶対位置への配置になるが、画面をスクロールした際も位置が固定されたままになる。
sticky position: relativeのように位置が指定され、fixedのように固定される。

次のようなケースでは、冒頭のサンプルのような意図しない重なりになる可能性が高いため注意が必要です。

  • positionプロパティの値をrelative、absoluteにし、かつz-indexの値をauto以外の整数値にしたとき
  • positionプロパティの値をfixed、stickyにしたとき

なぜなら、上記の条件下では、スタック文脈というものが形成されるからです。次に、スタック文脈を解説します。

スタック文脈とスタックレベル

スタック文脈(stacking contexts)スタックレベル(stack level)という概念は、z-indexを扱う上でとても重要なものです。

スタック文脈

スタック文脈とは、ある条件を満たした要素によって形成される階層構造(文脈)のことです。

スタック文脈を形成する条件は前述した通りですが、例えば、position: absoluteとz-indexに整数値を同時に設定した要素などが、スタック文脈を形成します。

ルート要素(HTML要素)は、ルートスタック文脈を形成しているため、HTML要素にz-indexを指定しなくとも、HTML要素はスタック文脈を形成します。

スタック文脈を形成する条件を満たした要素は、自身を基準としたローカルスタック文脈を形成します。これは、Illustratorでいうレイヤーやオブジェクトのような概念です。

それらのスタック文脈は、入れ子のように、内部にスタック文脈を持つことができます。

HTML要素がルートスタック文脈となり、その中にローカルスタック文脈が形成される。ローカルスタック文脈は、図のように入れ子構造をとることもできる。

このスタック文脈は、各階層における構造のようなものなので、ある要素が同時に複数のスタック文脈に属したり、他のスタック文脈内の要素が任意の要素の間にくることはできません。

また、スタック文脈を形成しない要素は、その親のスタック文脈に内包されることになります。

スタックレベル

スタックレベルは、同一のスタック文脈内での重なり順です。z-indexの値が、同一スタック文脈内でのスタックレベルになります。同じスタック文脈内に、同じスタックレベルを保つ要素がある場合、その構造内でより後方にある要素が前面に配置されます。

別の見方をすれば、異なるスタック文脈に属している要素同士の重なり順をz-indexの値でコントロールすることはできません。なぜなら、あるスタック文脈に属している要素は、そのスタック文脈の外に出ることはできないからです。

次回、改めて解説しますが、記事冒頭に掲載したサンプルの「意図しない重なり順」も、このことが原因で起きています。

重なり順の決定例

それでは、異なるスタック文脈に属している要素の重なり順が、どのように決定されるのか、サンプルを見てみましょう。それぞれの要素がスタック文脈を形成しています。HTML構造は次のようになっています。

このサンプルではすべてのdiv要素が、position: relative;またはposition: absolute;が指定され、かつz-indexに整数値が指定されています。つまり、それぞれのdiv要素が、別々のスタック文脈を形成しています。

<div class="z-5">.z-5(z-index:5)</div>
<div class="z-2">.z-2(z-index:2)</div>
<div class="z-4">
  .z-4(z-index:4)
  <div class="z-4-1">.z-4-1(z-index:1)</div>
  <div class="z-4-6">.z-4-6(z-index:6)</div>
</div>
div {
    padding: 10px;
    font-size: 12px;
}
.z-5 {
    position: relative;
    z-index: 5; /* スタック文脈を形成 */
    width: 400px;
    height: 100px;
    background-color: rgba(255,0,0,0.5);
}
.z-2 {
    position: absolute;
    z-index: 2; /* スタック文脈を形成 */
    top: 200px;
    width: 400px;
    height: 100px;
    background-color: rgba(255,0,0,0.5);
}
.z-4 {
    position: absolute;
    z-index: 4; /* スタック文脈を形成 */
    top: 50px;
    left: 100px;
    width: 200px;
    height: 240px;
    background-color: rgba(0,150,0,1);
}
.z-4-1 {
    position: absolute;
    z-index: 1; /* スタック文脈を形成 */
    top: 65px;
    left: 10px;
    width: 100px;
    height: 100px;
    background-color: rgba(0,0,255,1);
}
.z-4-6 {
    position: absolute;
    z-index: 6; /* スタック文脈を形成 */
    top: 55px;
    left: 120px;
    width: 150px;
    height: 150px;
    background-color: rgba(255,255,0,1);
}

.z-4の子要素.z-4-1z-index: 1;ですが、z-index: 2;.z-2よりも上に表示されます。

これは、同一のスタック文脈にある.z-4.z-2よりも上にあるためです。

実際にブラウザでレンダリングすると、次のようになります。

このようなイメージで立体的に捉えるとわかりやすいかもしれません。

次の点が、レンダリング時のdivの重なり順を決めるポイントになります。

  • .z-5.z-2そして.z-4はHTML要素のスタック文脈の中に配置される。
  • そして、.z-4のローカルスタック文脈の中に子要素.z-4-1.z-4-6が配置される。
  • .z-4-1.z-4-6が、.z-4が形成するローカルスタック文脈以外に配置されることはない。

スタック文脈やスタックレベルを親子関係のように考え、次のように表現することもできます。

HTML
┣.z-5 (5)
┣.z-4 (4)
┃ ┣.z-4-6 (4の6)
┃ ┗.z-4-1 (4の1)
┗.z-2 (2)

()内の値は、z-indexの値を表しています。数字が2つ並んでいるのは、div要素が親子関係になっていることを表します。.div(親要素のz-indexの値その要素のz-indexの値)となっています。

ルートスタック文脈に属するローカルスタック文脈が持つz-indexの値で重なり順が決まります。この場合、下から.z-2.z-4.z-5となります。

.z-4-1z-4-6は、.z-4で形成されるローカルスタック文脈に属するので、ルートスタック文脈に属する.z-5.z-4.z-2とは、文脈が異なります。

ですから、.z-4-6のz-indexの値が6だからといって、.z-5の上には配置されません。この点がポイントです。

まとめ

今回はz-indexを正しく指定するために必要な、次の3つの項目について説明しました。

  1. z-index
  2. positionプロパティとその値
  3. スタック文脈とスタックレベル

次回は、この原則を念頭に置きつつ、なぜ記事の冒頭にあったような失敗が起きたのかを仕組みから解説します。