Свойство position определяет, как элемент располагается на странице: остаётся ли он в обычном потоке документа или «вынимается» из него и позиционируется вручную через top/left/right/bottom. Понимание position — одна из базовых опор вёрстки: от него зависит поведение шапки сайта, всплывающих меню, рекламных блоков, модальных окон и многого другого.
Пять значений коротко:
static(по умолчанию) — элемент в обычном потоке,top/left/... не действуют.relative— элемент остаётся в потоке (место зарезервировано), но его можно визуально сместить; также создаёт контекст позиционирования для дочернихabsolute-элементов.absolute— элемент полностью вынимается из потока, позиционируется относительно ближайшего «позиционированного» предка.fixed— элемент вынимается из потока и закрепляется относительно окна просмотра (viewport), не двигается при прокрутке.sticky— гибридrelativeиfixed: ведёт себя как обычный элемент, пока не достигнет заданного порога прокрутки, а затем «прилипает» на этом месте.
Разберём каждое значение подробно.
html-разметка для урока:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Практика Position</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<nav>
<ul>
<li><a href="#">Главная</a></li>
<li>
<a href="#">Курсы</a>
<ul class="dropdown">
<li><a href="#">HTML</a></li>
<li><a href="#">CSS</a></li>
<li><a href="#">JavaScript</a></li>
</ul>
</li>
<li><a href="#">Контакты</a></li>
</ul>
</nav>
</header>
<main>
<h1>Изучаем position</h1>
<section>
<h2>Static / Relative</h2>
<div class="box">
Box
</div>
<p>
Этот текст расположен после блока .box и позволит увидеть,
как ведут себя элементы при использовании static и relative.
</p>
</section>
<section>
<h2>Absolute</h2>
<article class="article">
<div class="badge">
Badge
</div>
<div class="ad">
Реклама
</div>
<h3>Название статьи</h3>
<p>
Здесь находится текст статьи. Именно относительно этого
контейнера будут позиционироваться абсолютные элементы.
</p>
</article>
</section>
<section>
<h2>Абсолютное центрирование</h2>
<div class="wrap">
<div class="modal">
Modal window
</div>
</div>
</section>
<section>
<h2 class="section-title">
Первый раздел
</h2>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
</section>
<section>
<h2 class="section-title">
Второй раздел
</h2>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
</section>
<section>
<h2 class="section-title">
Третий раздел
</h2>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
<p>Lorem ipsum...</p>
</section>
</main>
<button class="cta">
Записаться
</button>
</body>
</html>Элемент остаётся в нормальном потоке документа, и свойства top/left/right/bottom на него не влияют вообще.
.box {
position: static; /* значение по умолчанию — указывать явно не нужно */
top: 20px; /* не сработает */
}Большинство элементов страницы (параграфы, заголовки, обычные блоки) работают именно в этом режиме — static не назначают явно, к другим значениям переходят только тогда, когда нужно особое позиционирование. Частая ошибка новичков — ожидать, что top/left подвинут static-элемент; для этого сначала нужно сменить position на что-то ещё.
Элемент с position: relative не вынимается из потока — его место остаётся зарезервированным, соседние элементы ведут себя так, будто ничего не изменилось. При этом его можно визуально сдвинуть через top/left/right/bottom.
.box {
position: relative;
top: 20px; /* визуально сдвигается вниз, но исходное место остаётся пустым */
}Вторая и более частая причина использовать relative — создание контекста позиционирования для дочерних элементов с position: absolute. Если внутри relative-родителя есть absolute-потомок, тот позиционируется относительно именно этого родителя (точнее, его padding box), а не относительно всей страницы.
.card {
position: relative; /* контекст для .badge */
}
.badge {
position: absolute;
top: 8px;
right: 8px;
}Если убрать position: relative у .card, .badge привяжется к ближайшему предку, у которого position отличен от static — а если такого предка вообще нет, то к самой странице (начальному containing block), и «уедет» в угол экрана, а не карточки.
Ключевое правило, которое легко перепутать: relative не вынимает элемент из потока (в отличие от absolute) — если вы хотите, чтобы элемент не занимал место в документе, relative для этого не подходит.
Элемент с position: absolute полностью удаляется из нормального потока — соседние элементы ведут себя так, будто его не существует. Его позиция вычисляется относительно ближайшего предка, у которого position отличен от static (этот предок называется containing block). Если такого предка нет — точкой отсчёта становится сама страница.
<article class="article">
<div class="ad">Реклама</div>
<h1>Заголовок статьи</h1>
<p>Текст статьи...</p>
</article>.article {
position: relative; /* контекст для .ad */
}
.ad {
position: absolute;
top: 12px;
right: 12px;
}Баннер .ad всегда остаётся привязанным именно к этой статье, где бы статья ни находилась на странице — потому что .article создаёт для него контекст позиционирования.
nav li {
position: relative;
}
.dropdown {
position: absolute;
top: 100%; /* сразу под пунктом меню */
left: 0;
display: none;
}
nav li:hover .dropdown {
display: block;
}top: 100% здесь означает «100% высоты родителя (li)» — то есть подменю выпадает ровно под пунктом навигации.
.wrap {
position: relative;
}
.modal {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}left: 50%/top: 50% смещают верхний левый угол модального окна в центр родителя, а transform: translate(-50%, -50%) дополнительно сдвигает сам элемент на половину его собственной ширины и высоты назад — в результате центр самого модального окна оказывается точно в центре родителя. Это стандартный и самый предсказуемый приём центрирования абсолютно спозиционированных блоков.
Проценты в left/top у absolute-элемента вычисляются от размеров containing block, а не от размеров самого элемента — отсюда и нужен transform для точного центрирования, а не просто left: 50% само по себе.
Некоторые CSS-свойства у предков (transform, filter, will-change: transform) создают собственный контекст позиционирования — если absolute-элемент вдруг ведёт себя не так, как ожидалось (привязывается не к тому предку), в первую очередь проверьте, нет ли у промежуточных предков подобных свойств.
z-index работает в пределах конкретного контекста наложения (stacking context) — если кажется, что z-index «не действует», возможно, родитель уже создал отдельный контекст наложения, и сравнение z-index происходит не с теми элементами, с которыми ожидалось.
Элемент с position: fixed вынимается из потока и закрепляется относительно viewport — окна браузера. При прокрутке страницы он остаётся на том же самом месте экрана, независимо от того, что происходит с остальным содержимым.
.cta {
position: fixed;
right: 20px;
bottom: 20px;
z-index: 999;
}Фиксированный header перекрывает верхнюю часть страницы, если явно не «отдать» ему место заранее:
header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 64px;
}
main {
padding-top: 64px; /* компенсация — иначе контент окажется под шапкой */
}Дополнительно полезно задать scroll-padding-top у html, чтобы переходы по внутренним якорным ссылкам (#section) тоже учитывали высоту фиксированной шапки и не «прятали» заголовок раздела под ней:
html {
scroll-padding-top: 64px;
}По умолчанию fixed всегда привязан к viewport, независимо от вложенности в DOM. Но если у какого-либо предка задано свойство, создающее новый контекст (transform, filter, perspective, will-change: transform), фиксированный элемент может неожиданно начать позиционироваться относительно этого предка, а не всего окна браузера. Если fixed-элемент ведёт себя не так, как ожидалось — в первую очередь проверьте стили промежуточных предков на предмет подобных свойств.
Для анимации fixed-элементов лучше использовать transform/opacity, а не изменение top/left — это позволяет браузеру задействовать более быстрый путь рендеринга (композиция на GPU) вместо пересчёта раскладки страницы. Фиксированные элементы не должны перекрывать интерактивный контент (кнопки, поля форм) — особенно критично на мобильных экранах, где свободного места и так немного.
position: sticky ведёт себя как обычный элемент в потоке документа (как relative) до тех пор, пока при прокрутке не достигнет заданного порога (обычно top: 0) — после этого он «прилипает» к этой позиции и остаётся видимым, пока пользователь не прокрутит мимо всего его ближайшего скроллящегося родителя.
<section>
<h2 class="section-title">Раздел А</h2>
<p>Много текста...</p>
</section>
<section>
<h2 class="section-title">Раздел Б</h2>
<p>Ещё много текста...</p>
</section>.section-title {
position: sticky;
top: 0;
background: white;
padding: 8px 0;
}Пока страница прокручивается в пределах раздела, его заголовок остаётся строго вверху видимой области — а как только начинается следующий раздел, его заголовок «выталкивает» предыдущий и сам занимает прилипшую позицию.
sticky-элемент требует хотя бы одного смещения (top, left, right или bottom) — именно оно задаёт порог, при достижении которого элемент «прилипает». Без указания хотя бы одного из этих свойств sticky фактически будет вести себя как обычный static.
У родителя задан overflow: hidden, overflow: auto или overflow: scroll. Прилипание работает относительно ближайшего скроллящегося предка — если у одного из предков задано подрезающее переполнение, оно может «сломать» прилипание, ограничив область, в которой элемент способен закрепиться, вплоть до полного отключения эффекта.
У родительского контейнера недостаточная высота. sticky-элемент прилипает и остаётся видимым только до тех пор, пока не «закончится» его непосредственный родительский блок — если родитель по высоте равен самому прилипающему элементу, то фактического «прилипания» просто не будет видно, потому что элементу некуда прилипать: он и так уже занимает всю высоту родителя.
Забыт explicit top (или другое смещение). Без указания хотя бы top: 0 браузер не поймёт, при каком именно пороге элемент должен начать прилипать.
fixed закрепляется относительно всего окна браузера и остаётся видимым на протяжении всей страницы. sticky закрепляется только в пределах своего родительского блока — как только пользователь прокручивает мимо этого родителя целиком, прилипший элемент прокручивается вместе с ним дальше, а не остаётся навсегда видимым, как это было бы с fixed. sticky идеально подходит для заголовков разделов и элементов сайдбара, которые должны быть видны только пока пользователь находится в пределах конкретной секции контента.
| Значение | В потоке документа? | Относительно чего позиционируется |
|---|---|---|
static |
Да | Не позиционируется, top/left не действуют |
relative |
Да (место зарезервировано) | Само себя (визуальное смещение от исходной позиции) |
absolute |
Нет | Ближайший предок с position ≠ static (или страница) |
fixed |
Нет | Окно просмотра (viewport), не меняется при прокрутке |
sticky |
Да, пока не сработал порог | Ближайший скроллящийся родитель, после порога — прилипает к viewport в его пределах |
- Какое значение
positionиспользуется по умолчанию у всех элементов, и что это значение означает практически? - Что делает
position: relativeпо отношению к обычному, статическому положению элемента? - Может ли элемент с
position: relative, смещённый черезtop, «вылезти» за пределы своего родителя и перекрыть соседние блоки? - К чему привязывается элемент с
position: absolute? - Что произойдёт, если у элемента с
position: absoluteнет ни одного позиционированного предка? - Чем
position: fixedпринципиально отличается отposition: absolute? - Что делает
position: sticky, и чем оно отличается отfixed? - Для чего в навигационном меню часто используют абсолютное позиционирование?
- Какое значение
positionлучше всего использовать, чтобы закрепить кнопку в правом нижнем углу экрана так, чтобы она оставалась видимой всегда, независимо от прокрутки страницы? - Почему понимание разных режимов позиционирования особенно важно при вёрстке интерфейсов?
Создай два одинаковых блока div с одинаковым текстом. Первому не задавай position вообще (оставь static по умолчанию) и попробуй задать ему top: 30px. Второму явно задай position: relative; top: 30px;. Сравни результат и словами объясни в комментарии к CSS-файлу, почему первый блок не сдвинулся.
Создай карточку товара шириной 300px с заголовком и описанием. Добавь в правый верхний угол карточки бейдж «Новинка», используя position: relative у карточки и position: absolute у бейджа.
- Создай горизонтальную навигацию с тремя пунктами; у одного пункта добавь вложенный список-подменю. Подменю должно появляться при наведении и выпадать точно под пунктом.
- Отдельно создай блок-«окно» (контейнер с
position: relativeи фиксированной высотой) и центрируй внутри него модальное окно черезabsolute+transform: translate(-50%, -50%).
- Создай кнопку «Наверх», закреплённую в правом нижнем углу экрана через
position: fixed, видимую при любой прокрутке страницы. - Создай фиксированную шапку сайта высотой
64pxи не забудь компенсировать перекрытие контента черезpadding-topуmain.
Создай страницу с тремя разделами (section), в каждом — заголовок и достаточно длинный текст для прокрутки. Сделай заголовки прилипающими сверху через position: sticky; top: 0; — так, чтобы при прокрутке заголовок текущего раздела оставался видимым, пока не начнётся следующий.
Собери законченную страницу:
headerс горизонтальной навигацией, у одного пункта — выпадающее подменю (absolute), самheaderзакреплён сверху черезsticky.mainс двумя статьями (static, обычное расположение) — каждая с заголовком, текстом и картинкой.- Кнопка в правом нижнем углу страницы, закреплённая через
fixed, всегда видимая при прокрутке.