z-index が効かない原因|数字を大きくしても前に出ないのは、親が別の重なりグループを作っているせいだった
z-indexに9999を書いても前に出てこない。
原因は「positionがstaticのまま」か「親が重なりのグループを作っている」かのどちらか。
確かめる順番をまとめた。
z-indexに9999を書いても、要素が前に出てこない。
原因はたいてい二つのどちらかで、「position が static のまま」か、「親が別の重なりのグループを作っている」かだ。
この記事は、その二つを順番に確かめる手順をまとめた。
CSSで重なりを直そうとして、数字だけを大きくしていく。
僕はその直し方が、いちばん遠回りだと思っている。
数字の大小が効くのは、同じグループの中だけだからだ。
まず確認する:position は static のままじゃないか
z-indexは、position が static(何も指定していない状態)の要素には効かない。
relative、absolute、fixed、sticky のどれかを付けて、はじめて重なり順の数字が意味を持つ。
MDNの解説でも、static の要素に z-index を書いても効果はない、とされている。
例外が一つある。
親が display:flex や display:grid のとき、その子要素(フレックスアイテム、グリッドアイテム)は、position が static でも z-index が効く。
「flex の中だけ効いたり効かなかったりする」と感じたら、この例外が関係しているかもしれない。
.menu {
position: relative; /* これが無いと z-index は無視される */
z-index: 10;
}次に疑う:親が「重なりのグループ」を作っている
position を付けても前に出ないとき、本命はこちらだ。
CSSには、スタッキングコンテキスト(重なりのグループ)という考え方がある。
グループの中の要素は、そのグループの中だけで重なり順を競う。
グループの外の要素とは、グループ全体の順位で比べられる。
たとえば、親Aが z-index:1 のグループで、その中の子に z-index:9999 を書いたとする。
別の親Bが z-index:2 のグループなら、Aの子がどれだけ大きな数字でも、Bより前には出られない。
子の9999は、あくまでAというグループの内側での順位だからだ。
MDNの例えでは、バージョン番号のように「親の番号-子の番号」で比べる、と説明されている。
2-0 より 1-9999 は小さい、という感覚だ。
グループを作ってしまう指定は、position と z-index だけじゃない
厄介なのは、z-index を書いていないのにグループができてしまう場合だ。
MDNによると、次のような指定でも新しいスタッキングコンテキストが作られる。
- position が absolute / relative で、z-index が auto 以外
- position が fixed / sticky(z-index の値に関係なく)
- opacity が 1 未満
- transform、scale、rotate、translate が none 以外
- filter、backdrop-filter、clip-path、mask など
- isolation:isolate
- flex / grid の子要素で、z-index が auto 以外
モーダルやドロップダウンが「なぜか親の後ろに隠れる」とき、親や祖先のどこかに transform や opacity が付いていないか見るといい。
アニメーションのために付けた transform が、重なり順の犯人だった、という形は起こりやすいと思う。
調べ方:DevToolsで親を順にたどる
原因を探すときは、前に出したい要素から親へ、一つずつ上へたどる。
見るのは三点だ。
- その要素の position は static ではないか
- 祖先に、上の一覧にある指定(transform、opacity、filter など)がないか
- 祖先同士の z-index の大小は、期待どおりか
ブラウザの開発者ツールで、親要素を選んで Computed(計算済み)の欄を見ると、position や transform、opacity の実際の値が一覧で確認できる。
数字を上げる前に、ここを一周するほうが早い。
直し方は二つ
一つ目は、前に出したい要素を、グループの外へ出すこと。
モーダルなら、HTMLの body 直下に置く。
親の transform の影響を受けなくなるので、重なり順がすっきりする。
二つ目は、グループの境界を自分で決めること。
ページ全体の重なりを管理したいなら、部品ごとの外側のラッパーに isolation:isolate を付けておく。
部品の内側でいくら大きな z-index を使っても、外へ漏れない。
僕は、数字を1000や9999まで膨らませるより、この「境界を決める」方が長持ちすると思っている。
.card {
isolation: isolate; /* card の中の z-index は外に影響しない */
}まとめ:数字を上げる前に見る順番
前に出ないときは、この順で見る。
- position は static のままじゃないか(flex / grid の子は例外)
- 祖先に transform や opacity など、グループを作る指定がないか
- グループ同士の順位は、どうなっているか
数字は、同じグループの中でだけ比べられる。
そう覚えておくと、z-index の迷子にならずにすむ。
出典
ほかにも書いています
note・スタンプ※noteは運営者が個人で書いているものです。
