BeAct Co., Ltd.

BLOG
社員ブログ

CSSの重ね順とz-indexを「レイヤー」で考える・3

こんにちは!今日もプログラミングを楽しんでいますか?

こんかいは、前回の記事で予告した「レイヤーの入れ子」について、さっそく説明していきましょう。


レイヤーの入れ子

このシリーズではCSSの重なりを「ペイントアプリのレイヤーUI」になぞらえて説明しています。さて、そのUIで、このような「入れ子状」のものを見たことはあるでしょうか?(画像はイメージエディタサービス Photopea のものです。)

Photopea のレイヤーUI

レイヤーの中に「フォルダ」を作ることで、その中にさらにレイヤーやフォルダを追加し続けることができる……まずはこの「入れ子構造」のイメージを、頭に置いてください。

これまでの説明との対比

上の図でのフォルダとレイヤーを、これまでに説明した「レイヤーコンテナ」や「レイヤー」に置き換えると、おおよそこのようになります。

ペイントアプリこれまでの説明
フォルダレイヤーコンテナ
レイヤーレイヤー

ただし一点だけ違いがあります。ペイントアプリのものとは異なり、これまでの説明における「レイヤーコンテナ」は、それ自身が「レイヤー」としての機能も持っています。完全に機能が分けられているわけではないことは覚えておいてください。

重ね順の仕組みを整理しよう

では、画像とレイヤーについて、改めて考えてみましょう。

次の図をご覧ください。ペイントソフトのレイヤーUIの例えに従って、左側に描画した画像、右側にレイヤーUIを配置しています(グレー地の項目は、レイヤーコンテナ=フォルダであるという意味です)。

ここで、「地面」「遠景」「青空」の3項目を見てみてください。これらは同じ階層にあるレイヤーコンテナとレイヤーです。同じ階層にあるということは、この3項目は「きょうだいレイヤー」とみなせますレイヤーコンテナはレイヤーでもあることを思い出してください!)。

レイヤーコンテナの順番を入れ替える

きょうだいレイヤーは、UIの上にあるほど手前に見えるというルールでしたね。手前から地面→遠景→青空の順番に重なっているのは、このためです。

では、「地面」と「遠景」のレイヤーコンテナを入れ替えてみましょう!

おおっと! 「遠景」が手前に描画されたことで、山が「地面」つまり木々や草原よりも手前に描画されてしまいました! この手のアプリを使っている人にとっては「あるある」のひとつではないでしょうか?

ここで少し立ち止まってみましょう。2つの山が一緒に「地面」の手前に描かれることについて、違和感を持つ方は少ないと思います。「山々」レイヤーコンテナは、「遠景」レイヤーコンテナの子どもなのですから、親同士の順番が入れ替わったら、それぞれの子ども同士の順番も入れ替わるのは、ごく自然なことと感じられることでしょう。
また、レイヤーコンテナの順番が変わったからといって、その子どもであるきょうだいレイヤー同士の順番が変わるわけではないということも、ごく自然なことと感じられるでしょう。「太陽」は「山々」の奥にあり続けていますし、「草原」は「木々」の奥にあり続けます。

……いま皆さんが感じた「それって当たり前でしょ」という気持ちを、ここで十分に噛み締めてください。

レイヤーコンテナの中で順番を入れ替える

さて、いったん順番をもとに戻して、今度は「遠景」レイヤーの子ども、「山々」と「太陽」を入れ替えてみましょう。

太陽が山の手前で輝いてしまいました! 「太陽」レイヤーが「山々」レイヤーコンテナより前になったことで、「グレーの山」「青い山」の2つよりも手前に描画されます。これも、とくに違和感を覚えることはないでしょう。「太陽」レイヤーと「山々」レイヤーコンテナがきょうだい同士である以上、「山々」の子どもたちはどう頑張っても「太陽」より前に描かれなくなってしまうからです。

また、この入れ替えをしても、「地面」や「青空」との重ね順にはなんの影響も及ぼしていないことも納得いただける挙動だと思います。

……いま皆さんが感じた「それって当たり前でしょ」という気持ちを、ここで十分に噛み締めてください。

{z-index: 10000;}が世に氾濫しているのはなぜか?

さあ、ここまでの話を踏まえて、肝心の「CSS の重ね順」に目を向けてみましょう。

実際の現場では、「とにかく前面に出したい要素」に対して z-index の数値をどんどん大きくしていく、という対応は珍しくありません。

モーダル、ドロップダウン、ツールチップ、ヘッダー……。これらをどうしても全面に描画しようとして 「とりあえず z-index: 9999; を付けたら直った」、あるいは「どんなに大きな値を設定しても、直すことができなかった」という経験がある方も多いでしょう。

ですが、ここで先に語った「ペイントアプリのレイヤーUIで、順番を変えた」ことを思い出してみてください。フォルダの中にあるレイヤーに、どれだけ「最前面!」と書き込んだとしても、そのフォルダ自体が他のフォルダより奥にあったら……そのレイヤーが画面全体で最前面に来ることはありません

これとまったく同じことが、CSSの z-index にも当てはまります。

z-index が効くのは、同じレイヤーコンテナを持つきょうだいレイヤー(要素)同士だけです!

つまり、{z-index: 10000;} は「画面全体で最前面に来る魔法の数値」ではありません。
それはただ、「そのレイヤーコンテナの中で一番前に来る」という指定にすぎないのです。

どんなに頑張ってもダメな例

さて、その典型例として、次のコード例を見てみましょう。

左上の「三」をクリックして、ハンバーガーメニューを開いてみてください。……おかしな重なり方をしていますね。しかしここで「ハンバーガーメニューを最前面に出したい!」と思うあまり、#hamburgerContent{z-index: 10000;} としても…

  #hamburgerContent {
    // 手前に出てほしい!
    z-index: 10000;
    position: relative;
    a {
      display: block;
    }
  }

願い虚しく、手前に来ることは決してありません。

親レイヤーコンテナの表示順には勝てない!

これを理解するには、「入れ子にしたレイヤーの重ね順」のルールを思い出す必要があります。

先のコード例が示していたレイヤーコンテナ/レイヤーは、実は以下の階層構造を持っています。そして、入れ子の親の表示順が低ければ、自身の z-index をいくら大きくしても前に出すことはできません!

この重ね合わせの入れ子という考え方を理解していただくために、今回までの長い説明をしてきたと言ってもいいくらい、これは重要なルールです。レイヤーコンテナ、すなわち重ね合わせコンテキストができる状況が起きるときは、このペイントアプリでの例を思い出していただければ幸いです。

次回は…

今までの説明で、「重ね合わせの順番は、親のレイヤーコンテナを超えることができない」ということを説明してきました。しかしこれは同時に、「レイヤーコンテナの重ね順をあらかじめ考えておけば(つまり、CSSを設計しておけば)、{z-index:10000;} などでコントロールする必要はない」という意味でもあります。

次回は、その「重ね順をあらかじめ設計する方法」について、お話していく予定です。お楽しみに!