Урок по треку HTML и CSS 7 мин

Позиционирование и z-index за 7 минут

static, relative, absolute, fixed, sticky и контекст наложения.

Свойство position решает две вещи: остаётся ли элемент в потоке и относительно чего считаются top, right, bottom, left (вместе их называют inset-свойствами, есть сокращение inset).

Пять значений

ЗначениеВ потоке?Относительно чего смещается
staticдане смещается, inset игнорируются
relativeда, место сохраняетсясвоей обычной позиции
absoluteнетближайшего позиционированного предка (containing block)
fixedнетобласти просмотра (viewport)
stickyдаведёт себя как relative, пока не упрётся в порог, затем «прилипает» внутри родителя

static стоит по умолчанию. «Позиционированный» значит любой position, кроме static.

relative

Элемент сдвигается визуально, но его место в потоке остаётся, соседи не двигаются. Чаще всего relative нужен не для сдвига, а чтобы стать точкой отсчёта для абсолютных детей:

.card { position: relative; }
.badge { position: absolute; top: 8px; right: 8px; }

absolute и containing block

Абсолютный элемент выпадает из потока: родитель «не видит» его высоту, соседи занимают его место. Координаты считаются от padding-box ближайшего предка с position не static. Если такого нет, от начального containing block: прямоугольника размером с viewport в самом начале документа (поэтому при прокрутке такой элемент уезжает вместе со страницей).

Ловушка: containing block для absolute и fixed создаёт и предок с transform, filter, perspective, contain: paint или will-change: transform. Отсюда баг «модалка с position: fixed внутри анимированного блока ездит вместе с ним»: она позиционируется уже не от viewport. Лечится выносом модалки в конец body (в React через портал) или нативным <dialog>, который рисуется в верхнем слое (top layer).

Если задать и left, и right при width: auto, абсолютный элемент растянется между ними. inset: 0 растягивает его на весь containing block.

fixed

Как absolute, но отсчёт от viewport, поэтому элемент не уезжает при прокрутке. Подходит для плавающих кнопок, тостов, но не для шапки, которая должна сначала скроллиться: для этого sticky.

sticky

thead th { position: sticky; top: 0; }

Липкий элемент остаётся в потоке и ведёт себя как relative, пока при прокрутке не дойдёт до порога (top: 0). Дальше он прилипает, но только в пределах своего родителя: когда родитель уезжает, уезжает и он.

Почему sticky «не работает»:

  • не задан порог (top, bottom и т.д.);
  • у какого-то предка между элементом и прокручиваемой областью стоит overflow: hidden или auto: тогда элемент липнет к этому предку, а он не скроллится;
  • родитель по высоте равен самому элементу, двигаться некуда.

z-index и контекст наложения

z-index задаёт порядок по оси Z, но работает только у позиционированных элементов, а также у flex- и grid-элементов (им position не нужен).

Главное понятие: контекст наложения (stacking context). Это изолированная «стопка»: элементы внутри сортируются только между собой, а наружу весь контекст выступает как одно целое со своим z-index. Его создают, например:

  • корневой элемент html;
  • позиционированный элемент с z-index не auto;
  • position: fixed и sticky;
  • opacity меньше 1, transform, filter, isolation: isolate;
  • flex- или grid-элемент с z-index не auto.
<div class="a" style="position:relative; z-index:1">
  <div class="popup" style="position:absolute; z-index:9999">Я внутри a</div>
</div>
<div class="b" style="position:relative; z-index:2">Я выше всего блока a</div>

Попап с z-index: 9999 всё равно окажется под .b: он заперт в контексте .a с z-index: 1. Повышать число бессмысленно, надо поднимать сам контекст или выносить попап наружу.

Порядок внутри одного контекста снизу вверх: фон контекста, элементы с отрицательным z-index, обычные блоки в потоке, плавающие, строчные, позиционированные с z-index: auto или 0, затем положительные z-index. При равных значениях выше тот, кто позже в DOM.

isolation: isolate создаёт контекст без побочных эффектов: удобно, чтобы z-index внутри компонента не «протекал» наружу.

Логические свойства

Вместо top/left можно писать inset-block-start и inset-inline-start. Они учитывают направление письма: в арабском интерфейсе (dir="rtl") inset-inline-start окажется справа.

Частые ошибки

  • Ставить z-index у элемента со static и ждать эффекта.
  • Поднимать z-index до 99999 вместо того, чтобы разобраться в контекстах наложения.
  • Забыть position: relative у родителя и получить бейдж в углу страницы.
  • Ставить overflow: hidden на обёртку липкой шапки.

Что спросят на собесе

  • Чем отличаются relative, absolute, fixed, sticky?
  • Относительно чего позиционируется absolute?
  • Почему z-index: 9999 не помогает поднять попап?
  • Что создаёт контекст наложения?
  • Почему не работает position: sticky?

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.