Вы когда-нибудь устанавливали z-index: 99999 для элемента в своем CSS, и он не появлялся поверх других элементов? Такое большое значение должно легко визуально разместить этот элемент поверх всего остального, при условии, что для всех различных элементов установлено либо меньшее значение, либо не установлено вообще. Веб-страница обычно представлена в двухмерном пространстве; однако, применяя определенные свойства CSS, вводится воображаемая плоскость оси Z для передачи глубины. Эта плоскость перпендикулярна экрану, и с ее помощью пользователь воспринимает порядок элементов, расположенных один над другим. Идея воображаемой оси Z, восприятия пользователем составных элементов, заключается в том, что свойства CSS, которые ее создают, объединяются, образуя то, что мы называем контекстом стекирования. Мы поговорим о том, как элементы «укладываются» на веб-странице, что контролирует порядок укладки, а также о практических подходах к «разложению» элементов при необходимости. О стекировании контекстов Представьте, что ваша веб-страница — это стол. Добавляя элементы HTML, вы кладете листы бумаги один за другим на стол. Последний помещенный лист бумаги эквивалентен последнему добавленному элементу HTML и располагается поверх всех остальных листов, помещенных перед ним. Это обычный поток документов, даже для вложенных элементов. Сам стол представляет собой корневой контекст стека, образованный элементом , который содержит все остальные папки. Теперь в игру вступают определенные свойства CSS. Такие свойства, как положение (с z-индексом), непрозрачность, преобразование и содержание), действуют как папка. Эта папка берет элемент и все его дочерние элементы, извлекает их из основного стека и группирует их в отдельный подстек, создавая то, что мы называем контекстом стека. Для позиционированных элементов это происходит, когда мы объявляем значение z-index, отличное от auto. Для таких свойств, как непрозрачность, преобразование и фильтр, контекст стекирования создается автоматически при применении определенных значений.
Попытайтесь понять следующее: как только лист бумаги (т. е. дочерний элемент) оказывается внутри папки (т. е. контекста стека родительского элемента), он никогда не сможет выйти из этой папки или поместиться между документами в другой папке. Его z-индекс теперь актуален только внутри его собственной папки.
На рисунке ниже бумага B теперь находится в контексте стопки папки B, и ее можно заказать только вместе с другими бумагами в папке.
Представьте, если хотите, что у вас на столе есть две папки:
.folder-a {z-индекс: 1; } .folder-b {z-индекс: 2; }
Давайте немного обновим разметку. Внутри папки A — специальная страница, z-индекс: 9999. Внутри папки B — обычная страница, z-индекс: 5.
.special-page { z-index: 9999; } .plain-page { z-index: 5; }
Какая страница сверху? Это .plain-страница в папке B. Браузер игнорирует дочерние документы и сначала складывает две папки. Он видит папку B (z-индекс: 2) и помещает ее поверх папки A (z-индекс: 1), поскольку мы знаем, что два больше одного. Между тем, страница .special-page, для которой установлено значение z-index: 9999, находится в нижней части стека, даже если ее z-index установлен на максимально возможное значение. Контексты стекирования также могут быть вложенными (папки внутри папок), создавая «генеалогическое древо». Применяется тот же принцип: ребенок никогда не сможет покинуть папку своих родителей. Теперь, когда вы поняли, как контексты наложения ведут себя как папки, группирующие и меняющие порядок слоев, стоит задаться вопросом: почему определенные свойства — например, преобразование и непрозрачность — создают новые контексты наложения? Вот в чем дело: эти свойства не создают контексты стекирования из-за того, как они выглядят; они делают это из-за того, как браузер работает под капотом. Когда вы применяете трансформацию, непрозрачность, фильтр или перспективу, вы говорите браузеру: «Эй, этот элемент может двигаться, вращаться или исчезать, так что будьте готовы!»
Когда вы используете эти свойства, браузер создает новый контекст стекирования для более эффективного управления рендерингом. Это позволяет браузеру независимо обрабатывать анимацию, преобразования и визуальные эффекты, уменьшая необходимость пересчета того, как эти элементы взаимодействуют с остальной частью страницы. Думайте об этом как о том, что браузер говорит: «Я буду обрабатывать эту папку отдельно, чтобы мне не приходилось переставлять весь стол каждый раз, когда что-то внутри него меняется». Но естьпобочный эффект. Как только браузер переносит элемент на свой собственный слой, он должен «сгладить» все внутри него, создавая новый контекст стекирования. Это все равно, что взять папку со стола и обращаться с ней отдельно; все внутри этой папки группируется, и браузер теперь рассматривает ее как единое целое, решая, что находится поверх чего. Таким образом, даже несмотря на то, что свойства Transform и Opacity могут не влиять на визуальное расположение элементов, они влияют, и это делается для оптимизации производительности. Некоторые другие свойства CSS также могут создавать контексты стекирования по тем же причинам. MDN предоставляет полный список, если вы хотите копнуть глубже. Их довольно много, и это лишь иллюстрирует, насколько легко случайно создать контекст стека, не зная об этом. Проблема «распаковки» Проблемы со штабелированием могут возникнуть по многим причинам, но некоторые из них встречаются чаще, чем другие. Модальные компоненты — это классический шаблон, поскольку они требуют переключения компонента на «открытие» на верхнем слое над всеми остальными элементами, а затем его удаления из верхнего слоя, когда он «закрыт». Я вполне уверен, что все мы сталкивались с ситуацией, когда мы открываем модальное окно, а оно по какой-то причине не появляется. Дело не в том, что он не открылся должным образом, а в том, что он находится вне поля зрения на нижнем уровне контекста стека. Это заставляет вас задаться вопросом: «Почему?» поскольку вы установили:
.оверлей { положение: фиксированное; /* создает контекст стекирования */ z-индекс: 1; /* помещает элемент на слой выше всего остального */ вставка: 0; ширина: 100%; высота: 100вх; переполнение: скрыто; цвет фона: #00000080; }
Это выглядит правильно, но если родительский элемент, содержащий модальный триггер, является дочерним элементом внутри другого родительского элемента, которому также присвоено значение z-index: 1, это технически помещает модальный элемент в подслой, скрытый основной папкой. Давайте рассмотрим этот конкретный сценарий и пару других распространенных ошибок контекста стекирования. Я думаю, вы увидите не только, как легко случайно создать стековые контексты, но и как ими неправильно управлять. Кроме того, способ возврата в управляемое состояние зависит от ситуации. Сценарий 1: Модальное окно в ловушке
Вы можете сразу увидеть свое модальное окно в ловушке низкоуровневого слоя и определить родительский элемент. Расширения браузера Умные разработчики создали расширения, которые помогут. Такие инструменты, как расширение Chrome «Инспектор контекста стекирования CSS», добавляют дополнительную вкладку z-index в ваши DevTools, чтобы показать вам информацию об элементах, которые создают контекст стекирования.
Расширения IDE Вы даже можете обнаружить проблемы во время разработки с помощью такого расширения для VS Code, которое выявляет потенциальные проблемы контекста стека непосредственно в вашем редакторе.
Распаковка и восстановление контроля После того, как мы определили основную причину, следующим шагом будет ее устранение. Есть несколько подходов, которые можно использовать для решения этой проблемы, и я перечислю их по порядку. Однако вы можете выбрать кого угодно на любом уровне; никто не может жаловаться или препятствовать другому. Измените структуру HTML Это считается оптимальным решением. Чтобы вы столкнулись с проблемой контекста стекирования, вы должны были разместить некоторые элементы в забавных местах в своем HTML. Реструктуризация страницы поможет вам изменить форму DOM и устранить проблему стекового контекста. Найдите проблемный элемент и удалите его из элемента-ловушки в разметке HTML. Например, мы можем решить первый сценарий, «Модал в ловушке», переместив контейнер .modal из заголовка и поместив его в элемент
отдельно.Заголовок
Основное содержание
Этот контент имеет z-индекс 2 и по-прежнему не охватывает модальное окно.
главный>
Модальный заголовок
Я ни за чем не стою. Моя позиция улучшилась в результате реструктуризации DOM.
Когда вы нажимаете кнопку «Открыть модальное окно», модальное окно располагается поверх всего остального, как и должно быть. См. «Сценарий пера 1: захваченное модальное окно (решение)» [раздвоение] Шойомбо Габриэля Айомиде. ОтрегулируйтеРодительский контекст стекирования в CSS Что, если элемент невозможно переместить, не нарушив макет? Лучше решить проблему: родитель устанавливает контекст. Найдите свойство (или свойства) CSS, отвечающее за запуск контекста, и удалите его. Если у него есть цель и его нельзя удалить, дайте родительскому элементу более высокое значение z-index, чем его родственные элементы, чтобы поднять весь контейнер. При более высоком значении z-index родительский контейнер перемещается вверх, а его дочерние элементы отображаются ближе к пользователю. Основываясь на том, что мы узнали в сценарии «Погруженный раскрывающийся список», мы не можем переместить раскрывающийся список из панели навигации; это не имело бы смысла. Однако мы можем увеличить значение z-index контейнера .navbar, чтобы оно было больше значения z-index элемента .content. .navbar { фон: #333; /* z-индекс: 1; */ z-индекс: 3; положение: относительное; }
Благодаря этому изменению .dropdown-меню теперь без проблем появляется перед содержимым. См. «Сценарий пера 2: Погруженный раскрывающийся список (решение)» [разветвленный] Шойомбо Габриэля Айомиде. Попробуйте порталы, если используете фреймворк В таких средах, как React или Vue, портал — это функция, которая позволяет отображать компонент за пределами его обычной родительской иерархии в DOM. Порталы похожи на устройства телепортации ваших компонентов. Они позволяют отображать HTML-код компонента в любом месте документа (обычно прямо в document.body), сохраняя при этом его логическую связь с исходным родительским элементом для свойств, состояния и событий. Это идеально подходит для выхода из контекстных ловушек стекирования, поскольку визуализированный вывод буквально появляется за пределами проблемного родительского контейнера. ReactDOM.createPortal( <Подсказка />, документ.тело );
Это гарантирует, что ваш раскрывающийся контент не будет скрыт за его родительским элементом, даже если родительский элемент имеет переполнение: скрыт или имеет меньший z-индекс. В сценарии «Обрезанная подсказка», который мы рассматривали ранее, я использовал портал, чтобы спасти всплывающую подсказку от переполнения: скрытый клип, поместив ее в тело документа и расположив над триггером внутри контейнера. См. сценарий «Перо 3: обрезанная подсказка (решение)» [раздвоение] Шойомбо Габриэля Айомиде. Представляем контекст стекирования без побочных эффектов Все подходы, описанные в предыдущем разделе, направлены на «извлечение» элементов из проблемных контекстов стекирования, но в некоторых ситуациях вам действительно понадобится или вы захотите создать контекст стекирования. Создать новый контекст стекирования легко, но у всех подходов есть побочный эффект. То есть, за исключением использования изоляции: изолировать. При применении к элементу контекст стекирования дочерних элементов этого элемента определяется относительно каждого дочернего элемента и внутри этого контекста, а не подвергается влиянию элементов за его пределами. Классический пример — присвоение этому элементу отрицательного значения, например z-index: -1. Представьте, что у вас есть компонент .card. Вы хотите добавить декоративную фигуру, которая будет находиться за текстом карты, но поверх фона карты. Без контекста стека на карточке z-index: -1 отправляет фигуру в нижнюю часть корневого контекста стека (вся страница). Это заставит его исчезнуть за белым фоном .card: См. Pen Negative z-index (проблема) [раздвоение] Шойомбо Габриэля Айомиде. Чтобы решить эту проблему, мы объявляем изоляцию: isolate на родительском .card: См. Pen Negative z-index (решение) [раздвоение] Шойомбо Габриэля Айомиде. Теперь элемент .card сам становится контекстом стекирования. Когда его дочерний элемент — декоративная фигура, созданная на псевдоэлементе :before — имеет индекс z: -1, он перемещается в самый низ родительского контекста стекирования. Как и предполагалось, он идеально расположен за текстом и поверх фона карточки. Заключение Помните: в следующий раз, когда ваш z-индекс выйдет из-под контроля, это будет ловушка контекста стека. Ссылки
Контекст стека (MDN) Z-индекс и контексты стекирования (web.dev) «Как создать новый контекст стекирования с помощью свойства изоляции в CSS», Натали Пина «Какого черта, z-индекс??», Джош Комо
Дальнейшее чтение о SmashingMag
«Управление CSS Z-индексом в больших проектах», Стивен Фрисон «Прикрепленные заголовки и полноразмерные элементы: сложная комбинация», Филип Браунен «Управление Z-Index в компонентном веб-приложении», Павел Померанцев «Свойство CSS Z-Index: комплексный взгляд», Луи Лазарис