Контекст наложения и z-index
Обновлено
06.11.2023Просмотров
234Время прочтения
15 мин.Сложность
Концепция контекста наложения это знакомая нам из школьной геометрии концепция расположения предмета на оси Z в трехмерном пространстве. Поскольку оси X и Y, как вам уже известно, располагаются в верхнем левом углу элемента\страницы и смотрят соответственно вправо и вниз, начало оси Z расположено фактически там же и направлено к пользователю. Легко догадаться, что увеличение значения на оси Z означает более близкую позицию к пользователю.
Из этого следует простое базовое правило — элементы с большим z-индексом располагаются ближе к пользователю и закрывают элементы с меньшим z-индексом.
Однако это слишком просто, и на практике это свойство CSS (z-index) работает несколько иначе. Во-первых, свойство применяется только к элементам, для которых заданы position: absolute, fixed, sticky, relative. Обычные статично позиционированные элементы не обрабатывают z-index. Во-вторых, по умолчанию элементы находятся на оси Z так же, как они находятся в потоке документа, то есть ниже находящийся в разметке элемент будет отображаться ближе к пользователю и перед выше стоящими элементами.
На скриншоте видно, что квадраты находятся в разметке друг за другом. Для них указано position: relative, а также некоторые значения top и left для наглядности. На странице они отображаются согласно описанным выше правилам: нижестоящие в разметке элементы находятся ближе к пользователю и закрывают собой вышестоящие.
Это и объясняет простым образом работу контекста наложения. Элементы, для которых присвоено любое значение position, отличное от static (а также свойства opacity со значением меньше 1, но об этом позже), создают контекст наложения. Соседние элементы имеют общий (родительский) контекст. Соответственно, поскольку все три дива имеют общего родителя, они имеют общий контекст.
Это самое важное, что необходимо понимать при работе с контекстом наложения, это — ключ к пониманию всей темы, и самое главное ограничение, которое имеется в z-index. Именно отсюда исходят проблемы начинающих разработчиков при работе с контекстом и при попытках расположить один элемент позади другого.
Но прежде чем рассказывать далее, вернёмся к примеру и изменим z-index для красного блока, укажем значение 1.
Как видите, теперь красный блок стал отображаться впереди синего, и это совершенно логично, потому что по умолчанию значение свойства z-index: auto, и это говорит о том, что элемент располагается на оси z согласно своему расположению в теле документа. Как только мы указываем z-index: 1, мы тем самым продвигаем его ближе к пользователю относительно других элементов, находящихся в данном контексте. Теперь, несмотря на то, что красный находится выше в документе, он будет отображаться ближе к пользователю. И теперь даже зелёный блок будет располагаться позади красного.
По-прежнему это происходит из-за того, что зелёный не имеет указанного значения z-index, а красный имеет. Но вновь всё меняется, когда мы указываем z-index теперь уже для зелёного блока:
Как видите, вновь зелёный отображается впереди красного. Теперь у них одинаковое значение z-index. В таком случае браузер снова смотрит на порядок расположения элементов в документе.
На основе вышеописанного можно составить следующее представление о работе свойства z-index: первоначально элементы располагаются по оси z исходя из своего положения в документе. Если для элемента указан z-index, элемент перемещается по оси ближе к пользователю и далее сравнивается с элементами, находящимися на том же уровне z-оси. Если два элемента имеют одинаковый z-index, они вновь сравниваются по своему положению в документе, и так далее.
Однако и это ещё не всё.
Z-index не является универсальным средством расположения элементов на оси z. Точнее, его работа не настолько простая, чтобы мы могли однозначно сказать, что чем больше индекс, тем ближе элемент к пользователю. Есть определённые правила, когда элемент с меньшим индексом может отображаться впереди элемента с большим. Подобное поведение зависит от непосредственно контекста наложения, о котором мы уже упоминали. Дело в том, что элементы, находящиеся по соседству друг с другом (являющиеся сиблингами), имеют общий контекст наложения. Другой ряд элементов, находящийся в соседнем контейнере-родителе, будет иметь свой контекст. Поэтому вы не сможете, сколько бы ни мучили свойство z-index, изменить порядок отображения элементов, лежащих в разных контейнерах, относительно друг друга.
Понятнее будет на примере. На скриншоте мы имеем два контейнера left и right, находящихся в одном контексте. Далее мы объявляем для них position:relative, тем самым определяя для каждого из них свой контекст. Затем мы пытаемся красный блок при помощи z-index расположить впереди желтого, сиреневого и голубого. Несмотря на то, что у красного самый большой z-index на странице, он отображается позади жёлтого и сиреневого (и голубого).
Это происходит из-за того, что весь right контейнер находится в разметке ниже контейнера left, а значит перед ним. Из этого следует, что весь его контекст наложения находится перед контекстом левого. Соответственно, мы можем при помощи z-index изменять положение красного, синего и зелёного между собой, но они всегда при любых значениях z-index будут позади контекста right. Также жёлтый, сиреневый и голубой могут изменять положение относительно друг друга, но они всегда будут впереди контекста left.
Для того, чтобы красный блок стал отображаться перед жёлтым и сиреневым, нам нужно изменить положение всего контекста left, задав ему индекс больший, чем у right:
Запомните, это единственный способ изменить положение блоков относительно друг друга. Поэтому, когда вы столкнётесь с подобной проблемой, не тратьте время на переопределение индексов у элементов, находящихся в разных контекстах.
В связи с подобной механикой работы контекста наложения в веб разработке есть негласное правило: выносить все попапы, всплывающие окна, кнопки и прочие вспомогательные элементы интерфейса в «верхнюю область видимости», в «верхний контекст», то есть размещать их напрямую в body.
Пример подобной разметки представлен ниже. Подразумевается, что в header, main, footer у нас лежат соответствующие части сайта, ну а все всплывающие\фиксированные элементы лежат отдельно напрямую в body.
<body>
<header></header>
<main></main>
<footer></footer>
<div class="overlay"></div>
<div class="register-popup"></div>
<div class="answer-popup"></div>
<div class="widget-btn"></div>
</body>
Следование этому правилу вам определённо будет полезно, когда вы захотите например показать попап регистрации и затемняющий фон overlay перед кнопкой виджета. Благодаря тому, что все эти элементы являются сиблингами и имеют один контекст наложения, вы без проблем сможете показать их в любом порядке посредством простого переопределения z-index.
Собственный контекст наложения создаётся не только при указании определённых значений position. Есть ещё несколько случаев, описанных в справочнике. Однако вы будете сталкиваться с ними крайне редко и заучивать все условия не нужно. В случае, если браузер при работе с контекстом отображает неожиданный для вас результат, обратитесь к справочнику и внимательно проверьте все условия, описанные в нём.
Отрицательный z-index
Свойство может также приобретать отрицательные значения, в этом случае в общем-то всё будет работать прежним образом. Стоит только учесть, что, как и в математике, отрицательные значения уменьшаются от нуля и увеличиваются к нулю, а значит -40 будет меньше -20.
На примере ниже мы указали отрицательные индексы -1 для синего и зелёного. Красный будет отображаться перед ними обоими, поскольку для него z-index не задан вообще. Затем будет располагаться зелёный, поскольку с синим у него одинаковый индекс, но в разметке он находится ниже. На самом заднем положении будет синий.
Опять же, чтобы изменить положение зелёного и синего, достаточно будет для зелёного задать индекс -2.
Последнее, о чём стоит сказать — элемент не может оказаться позади своего родителя, какой бы индекс мы ему не указывали. Это видно на простом примере, где у родителя есть фоновый цвет:
Вышеуказанные свойства top, left, right, bottom работают с любым позиционированием, кроме статичного, пожалуйста, запомните это! При этом свойства top, left являются приоритетными над right, bottom. В случае, если вы одновременно укажете и те, и другие стороны, именно top, left будут работать, а right, bottom будут проигнорированы: