Освоение z-index в CSS
z-index
— свойство, используемое для управления порядком расположения слоёв в документе. Элементы с большим значением z-index
располагаются над элементами с меньшим значением. Подобно тому, как оси x и y на странице определяют расположение элементов по горизонтали и вертикали, z-index
определяет, как они располагаются друг над другом по оси z.Наложение по умолчанию
При написании HTML элементы, расположенные ниже в документе, естественно, располагаются над элементами, расположенными выше.
<body>
<header class="site-header"></header>
<main class="site-content"></main>
<footer class="site-footer"></footer>
</body>
В этом фрагменте HTML footer
будет располагаться поверх области содержимого main
, которая будет располагаться поверх header
, если все они будут расположены так, чтобы перекрывать друг друга.
Перекрытие элементов может осуществляться с помощью комбинации свойств position
и смещения top
, right
, bottom
и left
.
Если для каждого из этих элементов установить position: absolute
, то все они будут располагаться друг над другом. footer
занимает последнее место в документе, поэтому по умолчанию он располагается поверх двух предыдущих элементов.
Если я использую свойства смещения, top
и left
, то мы можем увидеть порядок более наглядно.
.site-header, .site-content, .site-footer {
position: absolute;
width: 400px;
padding: 20px;
}
.site-header {top: 0; left: 0;}
.site-content {top: 50px; left: 50px;}
.site-footer {top: 100px; left: 100px;}
Контекст наложения
Хотя использование position: absolute
и создаёт элементы, перекрывающие друг друга, мы ещё не создали так называемый контекст наложения (stacking context).
Контекст наложения создаётся любым из следующих способов:
- элемент с абсолютной или относительной позицией и значением
z-index
, отличным отauto
- элемент
flexbox
со значениемz-index
, отличным отauto
- элемент с
opacity
меньше1
- элемент, для которого установлено значение
transform
, отличное отnone
Наиболее распространённым способом создания и использования контекста наложения является первый пример в этом списке, поэтому остановимся на нем подробнее.
Возвращаясь к предыдущему примеру, отметим, что у нас есть три элемента, расположенные друг над другом, но в данный момент y них нет значения css свойства z-index
.
CSS свойство z-index
позволяет управлять порядком наложения.
Если я установлю z-index: 1
для footer
, z-index: 2
для main
и z-index: 3
для header
, то порядок наложения по умолчанию может быть полностью изменён на противоположный.
На первый взгляд это выглядит довольно просто: чем выше z-index
, тем выше располагается элемент — так, z-index: 9999
всегда будет находиться поверх z-index: 9
. К сожалению, все несколько сложнее.
z-index
в контексте наложения
<header class="site-header blue">header</header>
<main class="site-content green">content
<div class="box yellow"></div>
</main>
<footer class="site-footer pink">footer</footer>
Если я добавлю .box
внутри контейнера site-content
и расположу его чуть дальше правого нижнего угла, то мы увидим, что он находится над зелёным боксом и под розовым боксом.
.box {
position: absolute;
bottom: -25px;
right: -25px;
z-index: 4; /* won't work :( */
width: 75px;
height: 75px;
border: 1px solid #000;
}
.site-header {top: 0; left: 0; z-index: -1;}
.site-content {top: 50px; left: 50px;}
.site-footer {top: 100px; left: 100px; z-index: 3;}
Основываясь на наших знаниях о z-index
, мы можем подумать, что для того, чтобы жёлтый блок оказался выше розового, мы можем просто установить большее значение z-index
.
Если я установлю z-index: 4
, что выше, чем z-index: 3
, мы не увидим никаких изменений. Обычно люди пытаются принудительно сложить значения, задавая огромное число, например 9999
, но это также не даёт никакого эффекта. Подобные значения z-index
, встречающиеся в кодовой базе, являются своеобразным запахом кода
, поэтому старайтесь избегать их, если это возможно.
Причина, по которой мы не можем получить желаемый результат — жёлтый блок поверх розового, заключается в том, как ведёт себя z-индекс
в контексте наложения.
Для того чтобы продемонстрировать это, давайте рассмотрим несколько более сложный пример, который я позаимствовал с сайта MDN.
<header class="site-header blue">
<h1>Header</h1>
<code>position: relative;<br/>
z-index: 5;</code>
</header>
<main class="site-content pink">
<div class="box1 yellow">
<h1>Content box 1</h1>
<code>position: relative;<br/>
z-index: 6;</code>
</div>
<h1>Main content</h1>
<code>position: absolute;<br/>
z-index: 4;</code>
<div class="box2 yellow">
<h1>Content box 2</h1>
<code>position: relative;<br/>
z-index: 1;</code>
</div>
<div class="box3 yellow">
<h1>Content box 3</h1>
<code>position: absolute;<br/>
z-index: 3;</code>
</div>
</main>
<footer class="site-footer green">
<h1>Footer</h1>
<code>position: relative;<br/>
z-index: 2;</code>
</footer>
.blue {background: hsla(190,81%,67%,0.8); color: #1c1c1c;}
.purple {background: hsla(261,100%,75%,0.8);}
.green {background: hsla(84,76%,53%,0.8); color: #1c1c1c;}
.yellow {background: hsla(61,59%,66%,0.8); color: #1c1c1c;}
.pink {background: hsla(329,58%,52%,0.8);}
header, footer, main, div {
position: relative;
border: 1px dashed #000;
}
h1 {
font: inherit;
font-weight: bold;
}
.site-header, .site-footer {
padding: 10px;
}
.site-header {
z-index: 5;
top: -30px;
margin-bottom: 210px;
}
.site-footer {
z-index: 2;
}
.site-content {
z-index: 4;
opacity: 1;
position: absolute;
top: 40px;
left: 180px;
width: 330px;
padding: 40px 20px 20px;
}
.box1 {
z-index: 6;
margin-bottom: 15px;
padding: 25px 10px 5px;
}
.box2 {
z-index: 1;
width: 400px;
margin-top: 15px;
padding: 5px 10px;
}
.box3 {
z-index: 3;
position: absolute;
top: 20px;
left: 180px;
width: 150px;
height: 250px;
padding-top: 125px;
text-align: center;
}
Здесь у нас, как и прежде, есть header
, footer
и main
, но внутри site-content
у нас есть три блока, которые были позиционированы и им был присвоен z-index
.
Рассмотрим сначала три основных контейнера — header
, footer
и main
.
header
имеет z-index
5, поэтому отображается расположенным над main
контентом, который имеет z-index: 4
. footer
имеет z-index
2, поэтому отображается под main
с более высоким z-index
4. Все хорошо? Хорошо.
С тремя блоками внутри контейнера main
все становится несколько запутанным.
box1
имеет z-index
6, но оказывается под header
с меньшим z-index
5.
box2
имеет z-index
1, но появляется над footer
, который имеет более высокий z-index
2.
Итак, что происходит?
Все это объясняется тем, что все значения z-index
определяются в контексте их родительского наложения. Поскольку родительский контейнер .site-content
имеет более высокий z-index
, чем footer
, любые позиционированные элементы внутри .site-content
оцениваются в этом контексте.
Хорошим способом представления порядка наложения в контексте наложения является представление о нем как о подпункте во вложенном упорядоченном списке.
- header:
z-index: 5
- main:
z-index: 4
- box1:
z-index: 4.6
- box2:
z-index: 4.1
- box3:
z-index: 4.3
- box1:
- footer:
z-index: 2
Поэтому, несмотря на то, что header
имеет z-index: 5
, а содержимое box1
имеет z-index: 6
, порядок рендеринга равен 4,6, что все равно меньше, чем 5. Таким образом, содержимое box1
отображается ниже header
.
Поначалу это немного запутанно, но с практикой все становится понятно!
z-index
работает только для позиционированных элементов
Если необходимо управлять порядком расположения элементов, это можно сделать с помощью z-index
. Однако z-index
будет действовать только в том случае, если элемент также имеет position
со значением absolute
, relative
или fixed
.
Точное размещение элементов с помощью position
отлично подходит для создания сложных макетов или интересных моделей пользовательского интерфейса, но часто возникает необходимость управлять порядком наложения элементов, не перемещая их с исходного места на странице.
В этом случае можно просто задать position: relative
, но не указывать значения top
, right
, bottom
или left
. Элемент останется на прежнем месте на странице, поток документа не будет прерван, а значения z-index
вступят в силу.
Вы можете использовать отрицательный z-index
Наложение элементов часто используется для создания сложных форм или компонентов пользовательского интерфейса. Часто это означает наслоение элементов друг на друга с постоянно увеличивающимся значением z-index
. Чтобы поместить элемент на слой ниже другого, ему достаточно иметь меньшее значение z-index
, причём это меньшее значение может быть и отрицательным.
Одна из областей, где это полезно, — использование псевдоэлементов и желание расположить их за содержимым родительского элемента.
В связи с тем, что контекст наложения работает, для элементов :before
или :after
требуется отрицательное значение z-index
, если они должны располагаться за текстовым содержимым своего родительского элемента.
Посмотрите на следующий Codepen и поэкспериментируйте с различными значениями z-index
.
Стратегия z-index
В заключение приведём простую стратегию применения z-index
по всему проекту.
Ранее мы использовали одноразрядные инкременты для значений z-index
, но что делать, если необходимо добавить новый элемент между двумя, имеющими значения z-index: 3
и z-index: 4
? Пришлось бы менять множество значений — возможно, во всей кодовой базе, что могло бы стать проблематичным и привести к поломке CSS в других частях сайта.
Для задания z-index используйте шаг 100
При работе с z-index
нередко можно встретить код, подобный этому:
.modal {
z-index: 99999;
}
На мой взгляд, это выглядит просто халтурно (и становится ещё хуже, если добавить к этому значению !important
). Подобные значения часто являются симптомом того, что разработчик не понимает контекста наложения слоёв и пытается заставить один слой быть поверх другого.
Вместо того чтобы использовать произвольные числа типа 9999
, 53
или 12
, мы можем систематизировать шкалу z-index
и привнести в процесс немного больше порядка. Это не потому, что у меня OCD разработчика. Честно.
Вместо того чтобы использовать для z-index
увеличение на единицу, я использую увеличение на 100.
.layer-one {z-index: 100;}
.layer-two {z-index: 200;}
.layer-three {z-index: 300;}
Я делаю это для того, чтобы сохранить порядок, а также для того, чтобы не забывать о множестве различных слоёв, используемых в проекте. Ещё одно преимущество заключается в том, что если необходимо добавить новый слой между двумя другими, то между ними можно выбрать 99 потенциальных значений.
При построении системы z-index
этот ручной подход является достаточно надёжным, но его можно сделать более гибким, если использовать возможности такого препроцессора, как Sass/SCSS.