Abschnitt 8 · Lektion 6
z-index und der Stacking Context
Sobald sich zwei Elemente überlappen, muss der Browser entscheiden, welches oben liegt. Er tut das nach festen Regeln, und die erste davon kommt ohne z-index aus.
Erst die Reihenfolge, dann die Position
<div class="feld">
<div class="kasten a">eins, steht zuerst im Markup</div>
<div class="kasten b">zwei</div>
<div class="kasten c">drei, steht zuletzt</div>
</div> .feld {
position: relative;
height: 220px;
font-family: system-ui, sans-serif;
}
/* Alle drei sind positioniert und haben keinen z-index.
Es entscheidet allein die Reihenfolge im Dokument. */
.kasten {
position: absolute;
width: 200px;
padding: 12px;
color: #ffffff;
}
.a {
top: 0;
left: 0;
background-color: #2563eb;
}
.b {
top: 40px;
left: 40px;
background-color: #15803d;
}
.c {
top: 80px;
left: 80px;
background-color: #b91c1c;
} Ohne weitere Angaben gilt: was später im Dokument steht, liegt oben. Im Beispiel sind alle drei Kästen gleich aufgebaut, und der letzte deckt die beiden anderen ab.
Dazu kommt eine zweite Regel: positionierte Elemente liegen über nicht positionierten. Ein Element mit position: relative liegt also über einem statischen Nachbarn, selbst wenn dieser später im Markup steht.
Das erklärt schon einen guten Teil der Fälle, in denen etwas überraschend oben oder unten liegt. Bevor du zu z-index greifst, lohnt deshalb die Frage, ob nicht einfach die Reihenfolge im HTML die falsche ist.
z-index braucht eine Position
<div class="feld">
<div class="kasten statisch">z-index: 999, aber static</div>
<div class="kasten positioniert">ohne z-index, aber positioniert</div>
</div> .feld {
position: relative;
height: 180px;
font-family: system-ui, sans-serif;
}
.kasten {
width: 240px;
padding: 12px;
color: #ffffff;
}
/* Ein hoher z-index an einem statischen Element wirkt
nicht. Die Eigenschaft gilt nur für positionierte
Elemente sowie für Flex- und Grid-Kinder. */
.statisch {
z-index: 999;
background-color: #b91c1c;
}
/* Positionierte Elemente liegen ohnehin über
nicht positionierten. */
.positioniert {
position: relative;
top: -40px;
left: 40px;
background-color: #15803d;
} z-index nimmt eine ganze Zahl, auch negative, und wirkt nur auf positionierte Elemente sowie auf Flex- und Grid-Kinder, die kommen in Abschnitt 9 und 10.
An einem statischen Element ist die Angabe wirkungslos. Sie ist gültig, sie wird nicht verworfen, und sie tut nichts.
Der Streifen beantwortet das mit zwei Reitern, und die Antwort steht im Unterschied zwischen ihnen. Zeig auf den roten Kasten: Im Reiter „Regeln” steht .statisch mit z-index: 999, nicht durchgestrichen und ohne Vermerk. Im Reiter „Werte” gibt es dagegen gar keine z-index-Zeile, denn der Streifen zeigt sie nur an Elementen, die auch positioniert sind. Am grünen Kasten daneben steht sie sehr wohl, dort mit dem Wert auto, und dieser Kasten liegt oben, obwohl er gar keine Zahl hat.
Wenn also ein z-index: 999 nichts bewirkt, ist die erste Frage: ist das Element überhaupt positioniert? Der Streifen beantwortet sie in seiner zweiten Zeile.
Zu den Zahlen selbst noch ein Wort: 999 und 9999 sind keine Lösung, sondern eine Kapitulation. Wer solche Werte vergibt, weiß nicht mehr, wogegen er anstinkt, und der Nächste muss dann 99999 nehmen. Eine kleine Skala mit drei bis vier Stufen, einmal oben in der Datei notiert, hält jahrelang.
Der Stacking Context
<div class="feld">
<div class="gruppe">
<div class="kind">z-index: 9999</div>
</div>
<div class="nachbar">z-index: 1</div>
</div>
<p>Nimm die Zeile mit opacity heraus, und das Kind liegt oben.</p> .feld {
position: relative;
height: 200px;
margin-bottom: 16px;
font-family: system-ui, sans-serif;
}
/* opacity unter 1 öffnet einen Stacking Context.
Alles darin wird zusammen als eine Ebene einsortiert,
und die hat selbst gar keinen z-index. */
.gruppe {
position: relative;
opacity: 0.99;
}
.kind {
position: absolute;
top: 0;
left: 0;
z-index: 9999;
width: 220px;
padding: 12px;
color: #ffffff;
background-color: #b91c1c;
}
.nachbar {
position: relative;
z-index: 1;
/* 20px und nicht mehr: .gruppe ist null Pixel hoch,
weil ihr einziges Kind absolut positioniert ist.
Der Nachbar startet deshalb ganz oben, und bei
einem größeren Wert überlappen die beiden
kaum noch. Zu sehen wäre dann nichts. */
top: 20px;
left: 60px;
width: 220px;
padding: 12px;
color: #ffffff;
background-color: #15803d;
} Und jetzt der Teil, wegen dem diese Lektion eine eigene ist.
Bestimmte Eigenschaften öffnen einen Stacking Context, also einen eigenen kleinen Stapel. Alles darin wird untereinander sortiert und danach als ein einziges Element in den umgebenden Stapel einsortiert.
Die Folge steht im Beispiel: Das Kind hat z-index: 9999, der Nachbar draußen nur z-index: 1, und trotzdem liegt der Nachbar oben. Nach außen zählt nicht die 9999, sondern der Platz der Gruppe, und die hat selbst gar keinen z-index.
Nachlesen kannst du das an beiden: Im Streifen steht am Kind z-index: 9999 und am Nachbarn z-index: 1. Beide Zahlen sind echt, sie treten nur nie gegeneinander an. Die Gruppe selbst lässt sich nicht antippen, sie ist nur eine Hülle ohne eigene Höhe; ihre Regel steht dafür blass in der Liste des Kindes, mit opacity: 0.99. Das ist die Zeile, die den eigenen Stapel aufmacht.
Einen Stacking Context öffnen unter anderem:
position zusammen mit einem z-index ungleich auto. Der bekannte Fall.
opacity unter 1. Der unbekannte, und deshalb steht er im Beispiel. Ein Element halb durchsichtig zu machen ändert die Stapelregeln für alles darin.
transform, filter, backdrop-filter, perspective, mix-blend-mode, will-change und isolation: isolate.
Die ersten davon kennst du schon: transform, filter, backdrop-filter, perspective und will-change sind dieselben, die in Lektion 8.4 dafür gesorgt haben, dass ein fixiertes Element plötzlich mitscrollt. Das ist kein Zufall, beides folgt daraus, dass ein solches Element ein eigenes Koordinaten- und Stapelsystem aufmacht.
Deckungsgleich sind die beiden Listen aber nicht, und die Abweichung geht nur in eine Richtung. opacity, mix-blend-mode und isolation öffnen nur den Stapel und machen keinen Bezugsrahmen für fixed. Umgekehrt gibt es keinen Fall: alles, was einen Bezugsrahmen für ein fixiertes Element aufmacht, öffnet auch einen Stacking Context.
isolation: isolate als Werkzeug
Bisher klang der Stacking Context nach einem Ärgernis. Er ist aber genauso oft die Lösung.
isolation: isolate öffnet einen Stacking Context, ohne sonst irgendetwas zu ändern. Keine Position, keine Transparenz, keine Verschiebung. Genau dafür gibt es die Eigenschaft.
Der Nutzen: Du kapselst eine Komponente ein. Wenn eine Kartenliste isoliert ist, können die z-index-Werte darin niemals mit der Kopfzeile der Seite konkurrieren, egal welche Zahlen jemand später innerhalb einer Karte vergibt. Die Liste als Ganzes hat einen Platz im Stapel, und alles Innere bleibt darin.
Das ist der Unterschied zwischen einem Stylesheet, in dem die Zahlen mit der Zeit wachsen, und einem, in dem sie klein bleiben. Isolierte Komponenten brauchen nur 1, 2 und 3, weil sie nur gegen ihre eigenen Geschwister antreten.
Die Reihenfolge beim Suchen
Wenn etwas nicht oben liegt, obwohl der z-index hoch ist, geh diese drei Fragen von oben nach unten durch.
Ist das Element positioniert? Ohne Position wirkt z-index nicht.
Liegt ein Vorfahr in einem Stacking Context? Geh den Baum nach oben und such nach opacity, transform, filter und den anderen aus der Liste. Findest du einen, ist dein Element darin gefangen, und der Wert muss an diesen Vorfahren.
Konkurrieren die beiden überhaupt im selben Stapel? Zwei Elemente in verschiedenen Stacking Contexts vergleichen ihre eigenen Zahlen nie miteinander. Es zählt der Platz ihrer jeweiligen Gruppen.
Zum Mitnehmen
z-index: 9999 liegt trotzdem unten, wenn sein Elternteil unten liegt. Innerhalb eines Stacking Context sind die Kinder gefangen, und nach außen zählt nur der Wert des Elternteils.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 3 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.