Оригинальный адрес:Woohoo, Джош спросил, приходи на AU.com/CSS/stack in…
Автор: Джош Комо, переводчик: Линь Хунху
Несанкционированное воспроизведение запрещено.
Изучите каскадные контексты CSS, механизм номер один, вводящий в заблуждение в CSS.
В CSS мы все знаем, что z-index можно использовать для управления иерархическим порядком HTML. Элементы с большими индексами будут размещены в самом верху страницы:
<style>
.box {
position: relative;
width: 50px;
height: 50px;
border: 3px solid;
background: silver;
}
.first.box {
z-index: 2;
}
.second.box {
z-index: 1;
margin-top: -20px;
margin-left: 20px;
}
</style>
<div class="first box"></div>
<div class="second box"></div>
Поскольку .first.box имеет большее значение z-index, чем .second.box , он отображается первым. Если бы мы удалили объявление z-index из кода, то .first.box был бы скрыт .second.box .
Но часто все не так просто. Иногда большой z-индекс не является непобедимым. Давайте посмотрим, что случилось с этим кодом, написанным Сяо Мином!
<style>
header {
position: relative;
z-index: 2;
}
.tooltip {
position: absolute;
z-index: 999999;
}
main {
position: relative;
z-index: 1;
}
</style>
<header>
My Cool Site
</header>
<main>
<div class="tooltip">
A tooltip
</div>
<p>Some main content</p>
</main>
Сяомин четко заявил, что z-индекс .tooltip: 999999, что больше, чем z-индекс заголовка: 2. Но почему заголовок все еще отображается вверху?
Перед демистификацией этой тайны нам нужно изучить точку зрения, называемые контекстами укладки. Это всего немного запутано, но это самый простой механизм CSS. В остальной части этой статьи мы узнаем, что они? Как они работают и как мы можем использовать их в нашем коде для удобства.
Для кого эта статья: для всех фронтенд-разработчиков, в чьих страхах доминировали z-индексы.
Слои и группы
Если вы когда-либо использовали программное обеспечение для редактирования графики, такое как PS или Figma, вы должны быть знакомы с концепцией иерархии:
На изображении ниже есть 3 разных слоя холста, как у лазаньи. Нижний слой - фото котенка. Поверх фото два слоя забавных деталей, а финальная композиция — бородатый котенок-ангел!
В PS мы можем объединить эти слои:
Как и папки, группа позволяет нам группировать ряд иерархий. Иерархии между комбинациями не могут быть смешаны вместе. Все уровни собак накладываются на уровни кошек.
Когда мы экспортируем окончательную композицию, мы вообще не видим кота, потому что все слои покрыты котом:
Опять же, иерархии CSS на самом деле работают почти так же, как PS: элементы группируются вместе вstacking contexts. Когда мы присваиваем элементу z-индекс, это значение будет конкурировать только с другими элементами в том же контексте. Z-индекс не является глобальным.
По умолчанию простой HTML-текст имеет только один контекст, включающий все узлы. Однако мы можем добавить больше контекста!
Есть много способов создать контекст, но наиболее распространенным является объединение двух объявлений, position и z-index:
.some-element {
position: relative;
z-index: 1;
}
Когда два объявления находятся вместе, открывается секретный переключатель: таким образом мы создаем новый контекст, в котором элемент .some-element и его дочерние элементы будут сгруппированы вместе.
Вернемся к предыдущему примеру:
<style>
header {
position: relative;
z-index: 2;
}
.tooltip {
position: absolute;
z-index: 999999;
}
main {
position: relative;
z-index: 1;
}
</style>
<header>
My Cool Site
</header>
<main>
<div class="tooltip">
A tooltip
</div>
<p>Some main content</p>
</main>
Сначала мы можем нарисовать контекстную структуру этого кода:
Хотя z-индекс элемента .tooltip равен 999999, это значение действует только в основном теге. Этот z-индекс может контролировать только то, отображается ли .tooltip выше или ниже тега p.
Глядя на корневой контекст, мы можем сравнить, где расположены заголовок и основные теги. Потому что z-индекс основного тега меньше заголовка.Таким образом, основной и его дочерние элементы отображаются под заголовком..
Измените приведенный выше пример
Итак, как нам изменить код Сяо Мина, чтобы всплывающая подсказка отображалась над заголовком? На самом деле это очень просто, нам вообще не нужно добавлять z-index к основному тегу:
Основной тег без z-индекса не создаст контекст. Теперь давайте посмотрим на контекстную структуру кода, и она будет выглядеть так:
Теперь, поскольку элементы заголовка и всплывающей подсказки находятся в одном контексте, изменения их z-индекса начинают битву, и наконец определяется победитель!
Уведомление: Здесь мы не говорим о родительско-дочерних отношениях этих элементов. Независимо от того, насколько сложна всплывающая подсказка, вложенная в другие элементы, браузеру важен только контекст наложения.
нарушать правила
В приведенном выше примере изменения кода мы просто удалили z-индекс из основного тега, потому что z-индекс вообще ничего не делал. Но что, если главному тегу действительно нужен z-индекс для создания контекста? Как это сделать, не удаляя z-index?
Согласно правилам CSS, к сожалению, мы не можем добиться желаемого с помощью других методов CSS: элементы в одном контексте никогда нельзя сравнивать с другими контекстами.
К счастью, есть и другие способы добиться такого эффекта, и нам нужно немного мозгов.
Мы можем переместить всплывающую подсказку из основного тега, повесить ее ниже тега body и использовать позиционирование CSS, чтобы всплывающая подсказка выглядела дочерней по отношению к заголовку.
Дополнительные способы создания контекстов стека
Мы видели, как создать контекст, комбинируя относительный абсолютный и z-индекс, но это не единственный способ! Мы также можем использовать эти методы:
- установить непрозрачность на значение меньше 1
- установить положение фиксированным или липким (в этом случае нет необходимости указывать z-индекс)
- Установите режим смешивания на умножение, резкий свет, разницу (обычный не работает 🙅)
- Добавление в z-index контейнеров display:flex или display:grid
- Используйте преобразование, фильтр, путь обрезки или перспективу
- Установите will-change на непрозрачность или преобразование
- Создайте контекст напрямую с помощью изоляции:isolation (подробнее об использовании этого скоро!)
- Посмотреть другие подробностиКонтрольный список внедрения MDN
В следующем примере мы используем will-change для создания контекста для основного тега, поэтому мы по-прежнему получаем предыдущий результат:
Распространенные заблуждения о z-индексе
Мы уже знаем, что для того, чтобы z-индекс вступил в силу, нам нужно установить относительную и абсолютную позицию, верно?
На самом деле это не совсем так, давайте взглянем на следующий код:
Второму блоку назначается z-индекс, и он отображается над другими блоками. Но мы не можем видеть, где объявлена позиция, верно!
В большинстве случаев z-index работает только с элементами с положением (относительным/абсолютным). Но в макете flexbox z-индекс дочерних элементов flex может действовать, даже если позиция статична~
Погладим еще раз. .
Есть одна странная вещь, над которой нам, возможно, стоит подумать еще немного.
В предыдущей аналогии с PS мы можем отличить концепцию группы от концепции иерархии: все видимые элементы представляют собой иерархию, а группа — это просто контейнер, помогающий составить иерархию.
В браузерах это понятие немного расплывчато. Все элементы с z-индексом создают контекст.
Обычно, когда мы решаем использовать z-index, мы хотим изменить только положение этого элемента в текущем родительском контексте. Мы не хотим создавать контекст для этого элемента! Нам нужно хорошенько подумать об этом.
Когда создается контекст, он сглаживает все дочерние элементы. Все подэлементы четко организованы, и мы, по сути, запираем их внутри.
Мы не должны думать о z-index просто как об инструменте для изменения порядка элементов, мы также должны думать о нем как о способе обернуть (сгруппировать) его дочерние элементы. Z-индекс не действует, если группа не создана.
Как мы видели в предыдущих примерах, контекст иногда может вызывать тонкие, трудно поддающиеся отладке ситуации. Не было бы лучше, если бы z-индекс можно было сравнивать глобально?
Я так не думаю, вот некоторые из моих причин:
Если у нас очень сложная структура, нам нужно присвоить z-индекс многим элементам, давайте взглянем на инфляцию z-индекса!
Хотя я не разработчик браузеров, я могу представить, что текущий дизайн помогает браузерам работать эффективно. Без текущего дизайна браузеру пришлось бы сравнивать множество элементов с z-index, а это требовало много дополнительной работы!
Когда мы понимаем контекст, мы можем использовать это, чтобы запечатать некоторые элементы. Это очень мощный паттерн для компонентно-ориентированных фреймворков, таких как React.
Последний пункт самый интересный, давайте посмотрим дальше!
Полностью закрытая абстракция с изоляцией
Теперь я собираюсь познакомить вас с одним из моих любимых и самых сложных свойств CSS: свойством изоляции, спрятанным сокровищем языка.
Вы можете использовать его следующим образом:
.wrapper {
isolation: isolate;
}
Когда мы используем это для объявления элемента, оно делает одну вещь: создает новый контекст.
Почему существует так много способов создания контекста? Ха-ха, поскольку все остальные методы неявно создают контекст, другие изменения могут изменить его результат. Но изоляция может создать контекст самым чистым и простым способом.
- Не нужно указывать z-индекс
- Может использоваться на статических элементах!
- Не влияет на отрисовку дочерних элементов
Опять же, потому что это потрясающе! изоляцию можно использовать на статически расположенных элементах! Это позволяет нам запечатать его дочерние элементы!
Давайте посмотрим на другой пример. Недавно я создал потрясающий компонент конверта.
При наведении мышки на отверстие буква будет перечеркнута вот так
Структура этой сборки конверта разделена на четыре части (слева направо: открывающая наклейка на обратной стороне конверта, задняя часть конверта, письмо, лицевая сторона письма)
Я упаковал эту структуру вместе с компонентами React, и она выглядит так (я использовал сдержанные стили для удобства просмотра)
function Envelope({ children }) {
return (
<div>
<BackPane style={{ zIndex: 1 }} /> 信的正面
<Letter style={{ zIndex: 3 }}> 信
{children}
</Letter>
<Shell style={{ zIndex: 4 }} /> 信封后面
<Flap style={{ zIndex: isOpen ? 2 : 5 }} /> 开口贴
</div>
)
}
(Вам может быть интересно, почему z-индекс открытого поста динамический, ведь он должен отображаться в конце письма при его открытии)
Хороший компонент React — это тот, который можно использовать где угодно, автономный корпус, например космический скафандр. В данный момент наш скафандр, к сожалению, пропускает воздух.
Когда я помещаю этот компонент рядом с заголовком z-index: 3, наша иерархия и заголовок путаются, как показано выше. Потому что наша оболочка Envelope окружена четырьмя уровнями div, но сама по себе контекста не создает.
Но мы можем добавить изоляцию: изолировать во внешний div компонента Envelope, чтобы компоненты можно было разделить на группу.
function Envelope({ children }) {
return (
<div style={{ isolation: 'isolate' }}>
<BackPane style={{ zIndex: 1 }} />
<Letter style={{ zIndex: 3 }}>
{children}
</Letter>
<Shell style={{ zIndex: 4 }} />
<Flap style={{ zIndex: isOpen ? 2 : 5 }} />
</div>
)
}
Так почему бы нам не использовать предыдущий метод position:relative;z-index:1 для создания контекста? Это связано с тем, что ожидается повторное использование компонентов React, а в противном случае z-index: 1 не гарантируется. Прелесть изоляции в том, что вы можете гарантировать, что компонент всегда будет гибким.
Поддержка браузера изоляции не новый знакомый, он имеет хорошую поддержку браузера. Его можно использовать в любом браузере, кроме Internet Explorer.
Если вам нужна поддержка в Internet Explorer, вы можете использовать transform: translate(0px)
Контекст отладки
К сожалению, я не нашел много инструментов, помогающих в контексте отладки.
Microsoft Edge имеет интересный"3D вид"Это может помочь показать структуру контекста.
С практической точки зрения, это выглядит фактом, я чувствую усталость, я не чувствую, что он может помочь мне найти элементы в приложении, это не помогает мне понять мое приложение в контексте отношений.
На самом деле, у меня есть еще одна маленькая идея, это offsetParent.
const element = document.querySelector('.tooltip');
console.log(element.offsetParent); // <main>
offsetParent возвращает ближайшего предка с нестатической (относительной, абсолютной, фиксированной) позицией.
ПРИМЕЧАНИЕ. Это не идеальное решение. Не все контексты выложены с позицией, и не все элементы с позицией создают контекст! Но этот метод является хотя бы отправной точкой для обнаружения.
Если у вас есть новый подход, свяжитесь со мной в Twitter! :twitter.com/JoshWComeau
Обновление 1:Феликс Беккер рассказывает мне однуПлагин VSCodeКод, который можно использовать для выделения сгенерированного контекста:
(Этот плагин можно использовать в файлах .css и .scss)
Обновление 2:Джузеппе Гургоне рассказал мне о плагине для Google Chrome, который может добавлять новые в devtools."z-индекс"
Обновление 3:Андреа Драготта создал потрясающий плагин для браузера, который позволяет нам видеть целую кучу очень полезной информации о z-индексе и контексте:
Это действительно потрясающая вещь, я недавно использовал этот плагин.