Проблема z-index
в том, что мало кто четко понимает как он на самом деле работает. В нём нет ничего сложного, но если вы никогда не уделяли времени чтению его спецификации, почти наверняка вы не в курсе важных аспектов.
Не верите мне? Посмотрим, сможете ли вы решить следующую проблему.
Проблема
В представленном ниже HTML у нас есть три элемента <div>
, каждый из которых содержит элемент <span>
. Каждый элемент, в свою очередь, имеет цвет фона – красный, зелёный и синий соответственно. Также каждый элемент <span>
имеет абсолютное позиционирование где-то около верхнего левого угла документа и немного перекрывает другие элементы <span>
, чтобы было видно какой элемент находится поверх другого. Для первого элемента <span>
значение свойства z-index
равно 1, а для других двух это свойство не задано.
Вот как выглядят HTML и основные стили CSS. Ниже я также добавил визуальное демо с Codepen с полным CSS.
<div> <span class="red">Red</span> </div> <div> <span class="green">Green</span> </div> <div> <span class="blue">Blue</span> </div>
.red, .green, .blue { position: absolute; } .red { background: red; z-index: 1; } .green { background: green; } .blue { background: blue; }
Задача состоит в следующем: попробуйте расположить красный элемент <span>
за синим и зелёным элементами, не нарушая следующие условия:
- Нельзя изменять и дописывать HTML-разметку
- Нельзя дописывать/менять свойство
z-index
у элементов - Нельзя дописывать/менять свойство
position
у элементов
Вы должны получить следующую картину:
Внимание! Не нажимайте на вкладку с CSS, чтобы не подсмотреть ответ.
Решение
Ответом является добавление непрозрачности к первому элементу <div>
(родителю красного элемента <span>
) со значением, чуть меньшим, чем 1. Вот CSS, который был добавлен к примеру выше:
div:first-child { opacity: .99; }
Если вы сейчас в шоке ломаете себе голову и не можете поверить, что прозрачность будет влиять на порядок наложения элементов, добро пожаловать в клуб. Я был потрясён точно также, когда я впервые наткнулся на эту ситуацию.
Надеюсь, остальная часть статьи немного прояснит ситуацию.
Порядок наложения
Z-index кажется таким простым: элементы с более высоким значением должны находиться выше элементов с более низким значением, верно? На самом деле, нет. Это часть проблемы с z-index
. С первого взгляда он кажется простым и большинство разработчиков не тратят время на изучение правил его применения.
Каждый элемент в HTML-документе может находиться как выше, так и ниже других элементов. Это называется порядком наложения. Правила этого наложения довольно чётко определены в спецификации, но, как я уже говорил, большинство разработчиков не полностью их понимают.
Когда свойства z-index
и position
не определены, правила очень просты: порядок наложения точно такой же, как и порядок появления в HTML, в основном. (OК, на самом деле всё немного сложнее, но пока вы не используете отрицательные значения свойства margin для перекрытия строчных элементов, вы, вероятнее всего, не столкнетесь с крайними случаями).
Если говорить о свойстве position
, то любые позиционированные элементы (и их потомки) отображаются выше любых непозиционированных элементов. (Говоря “позиционированный” элемент, имеется ввиду, что у элемента задано свойство position
, отличное от static
– relative
, absolute
или fixed
.)
И, наконец, если участвует свойство z-index
, то всё становится немного сложнее. Мы предполагаем, что элементы с более высоким значением z-index
находятся выше элементов, у которых значение z-index ниже, а также любые элементы, у которых задано значение z-index
, находятся выше элементов без z-index
. Но всё не так просто. В первую очередь, z-index
работает только у позиционированных элементов. Если вы попытаетесь задать свойство z-index
элементу без позиционирования, ничего не произойдёт. Во-вторых, значения свойства z-index
могут создать контексты наложения. Теперь, то, что в начале казалось простым, стало сложнее.
Контексты наложения
Группы элементов с общим родителем, которые вместе перемещаются вверх и вниз в порядке наложения, создают то, что называется контекстом наложения. Полное понимание контекста наложения – это ключ к осознанию того, как работают z-index
и порядок наложения.
В качестве корневого элемента, каждый контекст наложения имеет один элемент HTML. Когда на элементе формируется новый контекст наложения, этот контекст наложения заключает все его дочерние элементы в определенном месте в порядке наложения. Это значит, что если элемент содержится в контексте наложения внизу порядка наложения, нет никакого способа, чтобы заставить его находится выше другого элемента с другим контекстом наложения, находящегося выше в порядке наложения, даже если установить z-index
в миллиард!
Новые контексты наложения могут быть сформированы на элементе в одном из трёх случаев:
- Когда элемент является корневым элементом документа (элемент
<html>
) - Когда у элемента задано значение свойства
position
, отличное отstatic
, и значение свойстваz-index
, отличное отauto
- Когда элемент имеет значение свойства
opacity
меньше единицы
Первый и второй случай формирования контекста наложения имеют смысл и, как правило, понятны веб-разработчиками (даже если они не знают, как они называются).
Третий случай (непрозрачность) почти никогда не упоминался за пределами документов спецификаций W3C.
Определение позиции элемента в порядке наложения
На самом деле, определение глобального порядка наложения всех элементов на странице (включая границы, фоны, текстовые узлы и т.д.) крайне сложнО и выходит далеко за рамки этой статьи (опять же, я отсылаю вас к спецификации).
Но для большинства задач и целей, общее представление о порядке может далеко пойти и помочь сохранить развитие CSS предсказуемым. Давайте начнём с того, что нарушим порядок на отдельных контекстах наложения.
Порядок наложения в пределах одного контекста наложения
Вот основные правила, чтобы определить порядок наложения в одном контексте наложения (от низа к верху):
- Корневой элемент контекста наложения
- Позиционированные элементы (и их потомки) с отрицательными значениями свойства
z-index
(бОльшие значения “укладываются” выше меньших значений; элементы с одинаковыми значениями располагаются в том же порядке, что и в HTML) - Непозиционированные элементы (располагаются в том же порядке, что и в HTML)
- Позиционированные элементы (и их потомки) со значением
auto
свойстваz-index
(располагаются в том же порядке, что и в HTML) - Позиционированные элементы (и их потомки) с положительными значениями свойства
z-index
(бОльшие значения “укладываются” выше меньших значений; элементы с одинаковыми значениями располагаются в том же порядке, что и в HTML)
Примечание: позиционированные элементы с отрицательными значениями
z-index
будут находиться первыми в контексте наложения, что означает, что они будут отображаться позади всех остальных элементов. Из-за этого у элемента появляется возможность находиться позади своего родителя, что обычно невозможно. Это будет работать только тогда, когда родитель элемента имеет тот же контекст наложения и не является его корневым элементом.
Глобальный порядок наложения
Имея чёткое понимание того, как/когда формируются новые контексты наложения, а также представление о порядке наложения в контексте наложения, выяснить, где конкретный элемент появится в глобальном порядке наложения не так уж и трудно.
Ключом к тому, чтобы не споткнуться на этом пути, является возможность определять, когда новые контексты наложения формируются. Если вы устанавливаете элементу значение z-index
в миллиард и он не перемещается выше в порядке наложения, посмотрите на его генеалогическое дерево на предмет того, формирует ли любой из его родителей контекст наложения. Если образуют, то ваш z-index
в миллиард не даст никакой пользы.
Заключение
Возвращаясь к исходной задаче, я воссоздал его HTML-структуру, добавив комментарии внутри каждого тега, которые показывают его место в порядке наложения. Это порядок при исходных правилах CSS.
<div><!-- 1 --> <span class="red"><!-- 6 --></span> </div> <div><!-- 2 --> <span class="green"><!-- 4 --><span> </div> <div><!-- 3 --> <span class="blue"><!-- 5 --></span> </div>
Когда мы добавим правило opacity
к первому элементу <div>
, порядок наложения поменяется:
<div><!-- 1 --> <span class="red"><!-- 1.1 --></span> </div> <div><!-- 2 --> <span class="green"><!-- 4 --><span> </div> <div><!-- 3 --> <span class="blue"><!-- 5 --></span> </div>
span.red
был 6, а стал 1.1. Я использовал точку, чтобы показать, что сформировался новый контекст наложения и span.red
теперь является первым элементам в новом контексте.
Надеюсь, что вам теперь понятно, почему красный блок оказался позади всех остальных. Исходный пример содержал всего два контекста наложения, корневой и ещё один, сформированный элементом span.red
. Добавив непрозрачность родителю элемента span.red, мы создали третий контекст наложения и, как следствие, значение z-index
элемента span.red
теперь действует в рамках нового контекста. Из-за того, что первый элемент <div>
(тот, к которому мы применили непрозрачность) и его родственные элементы не позиционированы и не имеют установленного значения свойства z-index
, их порядок наложения определяется порядком в HTML, что означает, что первый элемент <div>
и все элементы в его контексте наложения располагаются позади второго и третьего элемента <div>
.
Источник