Как работает z-index в CSS: порядок наложения элементов

z-index решает, какой из перекрывающихся элементов окажется сверху - но это не просто число: работает он только на позиционированных элементах, а сравнение значений происходит не по всему документу, а внутри одного контекста наложения. Из-за этого больший z-index иногда неожиданно проигрывает меньшему.

Попробуй прямо сейчас: поменяй у box-a значение z-index на 3 - синий блок окажется поверх красного, хотя в коде идёт раньше и в разметке ничего не менялось, кроме одного числа.

Порядок наложения по умолчанию: кто сверху без z-index

Без единого z-index браузер решает, кто сверху, не только по порядку в HTML. Главное правило: позиционированный элемент (position: relative, absolute, fixed или sticky) всегда рисуется поверх непозиционированного соседа - даже если в разметке идёт раньше него. Как уже говорилось в уроке про основы position, position: static полностью выключает элемент из игры с z-index - такой элемент всегда останется в самом низу, независимо от порядка в коде. А вот если оба элемента позиционированы и ни у одного нет z-index (оба со значением по умолчанию auto), тогда действительно решает порядок в HTML - сверху окажется тот, что идёт позже.

.card { position: relative; } - даже без z-index одного этого достаточно, чтобы поднять элемент над непозиционированными соседями.

ожидание, что порядок в HTML сам решает, кто сверху - неверно, если хотя бы один из перекрывающихся элементов позиционирован, а другой нет.

z-index и контекст наложения: почему больший иногда проигрывает

z-index работает только на позиционированных элементах: чем больше значение, тем выше слой. Но сравнение чисел происходит не по всему документу сразу, а внутри одного КОНТЕКСТА НАЛОЖЕНИЯ. Как только позиционированный элемент получает z-index, отличный от auto, он создаёт собственный, отдельный контекст - и все его потомки сравниваются друг с другом только внутри этого контекста, не участвуя напрямую в сравнении с элементами снаружи. Контекст наложения создаёт не только сочетание position + z-index: то же самое делают opacity меньше 1, а также transform, filter, perspective или will-change со значением, отличным от none - это ровно те же свойства, которые в уроке про absolute превращали даже статичного предка в точку отсчёта для потомков.

Отсюда классическая ловушка: пусть у контейнера A задан position: relative; z-index: 1, а глубоко внутри него - элемент с z-index: 9999. У соседнего контейнера B - position: relative; z-index: 2, а внутри - самый обычный элемент с z-index: 1. При перекрытии победит элемент из контейнера B, а не 9999 из A - потому что сначала сравниваются сами контейнеры (2 против 1), и только после этого - их содержимое. Число 9999 внутри A вообще не попадает в сравнение с содержимым B, ведь они находятся в разных контекстах наложения.

.modal { position: fixed; z-index: 1000; } - явный z-index на самом верхнеуровневом контейнере, а не на глубоко вложенных элементах внутри него.

.deep-child { z-index: 9999; } - огромное число глубоко внутри вложенного элемента не спасёт, если у самого контейнера z-index ниже, чем у соседнего контекста.

Отрицательный z-index: элемент "позади" - но позади чего именно

z-index может быть отрицательным - типичный приём для декоративного фона позади текста. Но здесь есть неочевидная ловушка: если у контейнера задан только position: relative, а собственный z-index НЕ задан, контейнер сам не образует контекст наложения. Тогда его потомок с отрицательным z-index "убегает" в контекст более высокого уровня - и может оказаться полностью скрытым за собственным фоном контейнера, хотя по логике должен быть только чуть позади содержимого. Чтобы отрицательный z-index вёл себя предсказуемо, самому контейнеру нужен явный z-index (подойдёт даже z-index: 0) вместе с position - тогда контейнер образует собственный контекст, и отрицательный потомок аккуратно ложится между фоном контейнера и обычным содержимым, как и ожидается.

.container { position: relative; z-index: 0; } вместе с .decor { position: absolute; z-index: -1; } внутри - надёжно работающий приём "фон позади контента".

.container { position: relative; } без z-index, а внутри .decor { z-index: -1; } - decor рискует полностью исчезнуть за фоном самого контейнера.

Из истории z-index был частью CSS уже в CSS2, который W3C выпустил как полноценную Рекомендацию 12 мая 1998 года. Но точный алгоритм того, как именно вкладываются и рисуются контексты наложения (нынешнее Appendix E спецификации), был официально закреплён в статусе Рекомендации только в CSS 2.1 - спустя более 13 лет, лишь 7 июня 2011 года. Всё это время браузерам приходилось самим доводить до единообразия мелкие детали наложения без окончательно формализованного текста спецификации под рукой - неудивительно, что контексты наложения до сих пор остаются одной из самых частых тем путаницы в CSS.

Частые ошибки новичков

ОшибкаЧто происходитКак исправить
Задают z-index элементу без position z-index полностью игнорируется браузером Сначала задать position: relative/absolute/fixed/sticky - только потом z-index
Пишут z-index: 9999 в глубоко вложенном элементе Проигрывает, если у его контейнера z-index ниже, чем у соседнего контекста наложения Поднимать z-index у самого контейнера, а не только у элемента глубоко внутри
Дают контейнеру только position: relative без z-index, а потомку - отрицательный z-index Потомок может полностью скрыться за фоном контейнера Задать контейнеру явный z-index (например, 0), чтобы он образовал собственный контекст
Полагаются только на порядок в HTML при перекрытии Порядок решает лишь среди элементов без z-index на одном уровне - в остальных случаях решают position и контекст наложения Учитывать одновременно position, z-index и контекст наложения, а не только порядок разметки

Шпаргалка

Что нужноКак сделать
Поднять элемент над непозиционированными соседямиposition: relative (даже без z-index)
Явно задать порядок среди позиционированных элементовz-index: число (чем больше - тем выше)
Создать собственный контекст наложенияposition ≠ static + z-index ≠ auto (или opacity < 1, transform/filter/will-change ≠ none)
Спрятать элемент позади содержимого контейнера предсказуемоотрицательный z-index у потомка + явный z-index у самого контейнера
z-index на непозиционированном элементене работает вообще - браузер его игнорирует

Коротко: отвечаем на вопросы

Почему z-index не работает на моём элементе?

Скорее всего, у элемента не задан position (или он равен static). z-index действует только на позиционированных элементах - relative, absolute, fixed или sticky. На элементе с position: static он полностью игнорируется браузером.

Почему больший z-index иногда всё равно проигрывает меньшему?

Сравнение z-index происходит только внутри одного контекста наложения. Если элемент с большим z-index находится внутри контейнера с более низким z-index, чем у соседнего контейнера, он проиграет содержимому этого соседа - потому что сначала сравниваются сами контейнеры, а не глубоко вложенные значения.

Можно ли сделать z-index отрицательным?

Да, отрицательные значения используют, чтобы спрятать элемент позади обычного содержимого контейнера - например, для декоративного фона. Но чтобы это работало предсказуемо, у самого контейнера должен быть явно задан свой z-index (не только position) - иначе потомок может полностью скрыться за фоном контейнера.

Категория
css
Поделиться в социальных сетях

Отправить комментарий

0 Комментарии

Отправить комментарий (0)

#buttons=(Хорошо) #days=(20)

Мы используем файлы cookie для улучшения работы сайта. Подробнее
Хорошо
×