Что такое z-index?
Наши мониторы — это двумерные плоские поверхности с кучей пикселей. Но в вебе есть и третье измерение. Например, каждый из нас видел, как модальное окно всплывает «над» остальным контентом на первый план.
Представьте, что экран — это портал в трехмерный мир браузера. Визуализируем это пространство как комнату. Дальняя от нас стена называется «холст» (canvas).
Самые близкие к холсту пиксели (самые удаленные от нас) браузер рисует первыми. Каждый новый слой отображается поверх уже окрашенного.
Свойство z-index
определяет порядок отображения блоков в этой иллюзорной трехмерности. По умолчанию все элементы имеют нулевой индекс и отрисовываются в том порядке, в котором находятся в DOM. Мы можем приближать их к себе или отдалять.
В спецификации CSS Position & Layout есть вот такая схема:

На первый взгляд, z-index
— это очень простое свойство: чем он больше, тем ближе слой к пользователю. Однако в его реализации есть несколько нюансов, которые вызывают много путаницы.
Прежде всего, индекс будет работать только на спозиционированном элементе (свойство position
отличается от static
). Для элемента, находящегося в обычном потоке, эффекта не будет.
Введение в контекст наложения
Второе условие еще более непонятно: действие z-index
ограничено контекстом наложения, который включает в себя корневой html-узел и всех его потомков.
Базовый контекст наложения создается узлом html
. По умолчанию к нему принадлежат и все остальные элементы в документе. Однако любой из них также может стать корневым узлом локального контекста.
Создать новый контекст можно несколькими способами:
- установить абсолютное или относительное позиционирование, а также любой
z-index
кромеauto
; - установить фиксированное позиционирование (
fixed
илиsticky
); - установить прозрачность меньше 1;
- использовать свойства
transform
илиwill-change
.
Полный список способов создания контекстов можно найти на MDN.
Рассмотрим три HTML-дерева. Нижняя часть — это корневой узел, а потомки уложены сверху. Предположим, что все три корня находятся прямо внутри body
.

Это будет выглядеть примерно так:
See the Pen by Benjamin Johnson (@benjaminj6) on CodePen.
Не ожидали?
Прежде всего, почему blue-child-2
находится внизу? Ведь его z-index
равен миллиону, он должен самым верхним, не так ли?
Почему green-child-1
наверху, хотя его z-index
всего 2? Разве больший индекс не должен перекрывать меньший?
Начнем с green-child-1
. Его родитель — green-parent
— имеет z-index: 999
, поэтому мы предполагаем, что он поднимется на самый верх. Но z-index
не влияет на элементы с position: static
. Это означает, что green-child-1
является частью корневого контекста наложения, в котором, кроме него, есть еще red-parent
и blue-parent
. Он имеет более высокий z-index
, чем они, поэтому находится выше.
Красный и синий корневые блоки создают собственные локальные контексты наложения. Почему blue-child-2
находится ниже, чем red-parent
, несмотря на больший z-index
? Дело в том, что это свойство управляет положением слоя только в локальном контексте. blue-child-2
, безусловно, будет выше всех дочерних элементов blue-parent
. Но контекст red-parent
выше, чем контекст blue-parent
, поэтому все красные потомки всегда будут выше синих.
Чтобы поднять blue-child-2
выше red-parent
придется изменить структуру HTML и извлечь его из локального контекста наложения в корневой (или в другой локальный, который будет выше красного). Но помните, что такие изменения в больших проектах могут привести к непредвиденным последствиям.
Многие библиотеки компонентов используют интерфейс с большим количеством слоев (всплывающие подсказки, модальные окна), добавляя их к тегу body
, а не внутрь самого компонента. Это позволяет обойти подводные камни стека наложения и гарантирует, что нужные элементы всегда будут на самом верху.
Вывод
z-index
иногда преподносит сюрпризы, но если вы знаете принципы его работы, то вполне можете предвидеть все возможные эффекты. 99% всей путаницы связаны с непониманием двух разобранных выше правил.
z-index
работает только на позиционированных элементах;z-index
работает внутри текущего контекста наложения.
Зная об этих тонкостях вы легко сможете создавать грамотные многоуровневые интерфейсы.
2 комментария
position: static
, в этом случаеz-index
не будет работать.