От автора: каждый раз, когда я работаю над компонентом, которому требуется абсолютное позиционирование, я спрашиваю себя: действительно ли это необходимо? Я заметил несколько вариантов использования, в которых нет необходимости в position:absolute. Мне показалось это интересным, и захотел, задокументировать варианты использования, с которыми я обычно сталкивался, работая над проектами.
В этой статье я рассмотрю несколько вариантов использования, в которых абсолютное позиционирование не требуется.
Вступление
Если мы вернемся на 5 лет назад, CSS flexbox был все еще новым и не мог использовать надлежащих отступов. CSS-grid тогда даже не поддерживалась. В конце концов, мы использовали позиционирование CSS для достижения желаемого макета. Однако в наши дни некоторые из этих вариантов использования можно решить с помощью CSS flexbox или grid.
На днях мы работали над фронтендом целевой страницы, и мой партнер спросил меня о проблеме с макетом, с которой он столкнулся. Проблема возникла из-за position:absolute, я попытался помочь, и мы исправили эту проблему, даже не используя позиционирование CSS.
Случаи использования с примерами
Наложение в карточке
Когда у нас есть карточка, содержащая текст поверх изображения, мы часто используем position:absolute для размещения содержимого поверх изображения. В CSS-сетке это больше не требуется.
Это типичная карточка с текстом поверх изображения. Вот HTML:
1 2 3 4 5 6 7 8 9 |
<article class="card"> <div class="card__thumb"> <img src="assets/mini-cheesecake.jpg" alt=""> </div> <div class="card__content"> <h2><a href="#">Title</a></h2> <p>Subtitle</p> </div> </article> |
Чтобы разместить контент поверх изображения, нам нужно позиционировать .card__content абсолютно.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.card { position: relative; } .card__content { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: linear-gradient(to top, #000, rgba(0, 0, 0, 0) bottom/100% 60% no-repeat; padding: 1rem; } |
В приведенном выше примере нет ничего плохого, но почему бы не использовать более простое решение? Давайте исследуем это.
Первым делом нужно добавить display:grid в компонент карты. Нам не нужно устанавливать какие-либо столбцы или строки.
1 2 3 4 5 6 7 8 9 10 |
.card { position: relative; display: grid; } .card__content { display: flex; flex-direction: column; justify-content: flex-end; } |
По умолчанию сетка CSS автоматически создает строки на основе содержимого. В карте у нас есть два основных элемента, поэтому у нас есть две строки контента. Чтобы перекрыть контент с изображением, нам нужно разместить их оба в первой области сетки.
1 2 3 4 5 |
.card__thumb, .card__content { grid-column: 1/2; grid-row: 1/2; } |
Более того, мы можем использовать сокращение grid-area:
1 2 3 4 |
.card__thumb, .card__content { grid-area: 1/2; } |
Наконец, мы также можем использовать grid-area: 1/-1. -1 представляет собой последний столбец и строку в сетке.
Card Tag
Основываясь на предыдущем примере, мы хотим расширить его и включить тег в левую верхнюю часть карты. Для этого нам нужно использовать ту же технику grid-area: 1/-1, но мы не хотим, чтобы тег заполнял всю карту, верно?
Чтобы исправить это, нам нужно указать тегу, чтобы он выровнялся по началу своего контейнера.
1 2 3 4 5 |
.card__tag { align-self: start; justify-self: start; /* Other styles */ } |
Познакомьтесь с тегом, который расположен над его родительским элементом, без использования position:absolute. Демо.
Herо-раздел
Еще один идеальный вариант использования — это hero-раздел с содержимым, перекрывающим изображение.
Это похоже на пример карты, но с другим стилем для самого содержимого. Прежде чем погрузиться в современный подход, давайте на минутку задумаемся о том, как это сделать с помощью абсолютного позиционирования. У нас есть три слоя: изображение, градиент наложения и содержание.
Есть разные способы реализовать это. Если изображение носит чисто декоративный характер, можно использовать background-image. В противном случае мы можем использовать элемент img.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
.hero { position: relative; min-height: 500px; } .hero__thumb { position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover; } /* The overlay */ .hero:after { content: ""; position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: #000; opacity: 0.5; } .hero__content { position: absolute; left: 50%; top: 50%; z-index: 1; transform: translate(-50%, -50%); text-align: center; } |
Вот как мы можем реализовать hero-раздел, используя абсолютное позиционирование. Однако мы можем добиться большего. Давайте исследуем современный подход.
Во-первых, нам нужно добавить display:grid к hero-элементу. После этого мы применим ту же концепцию, что и в компоненте карты grid-area:1/-1, к каждому прямому дочернему элементу.
К сожалению, нам нужно будет использовать фиксированную высоту для hero-секции, чтобы сработало правило .hero__thumb. (Дочерний элемент с height:100% требует, чтобы его родительский элемент имел явную фиксацию height, а не min-height).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
.hero { display: grid; height: 500px; } .hero__content { z-index: 1; /* [1] */ grid-area: 1/-1; display: flex; flex-direction: column; margin: auto; /* [2] */ text-align: center; } .hero__thumb { grid-area: 1/-1; object-fit: cover; /* [3] */ width: 100%; height: 100%; min-height: 0; /* [4] */ } .hero:after { content: ""; background-color: #000; opacity: 0.5; grid-area: 1/-1; } |
Я хочу пройтись по пронумерованным строкам кода, поскольку они важны:
Мы можем использовать z-index сетки или flex элемента. Вообще не нужно добавлять position:relative.
Поскольку .hero__content это элемент сетки, использование margin:auto будет центрировать его по горизонтали и вертикали.
Не забывайте включать object-fit:cover при работе с изображениями.
Я использовал для hero-изображения min-height:0 в том случае, если использовалось большое изображение. Это сделано для того, чтобы заставить CSS-сетку учитывать height:100% и избегать увеличения размера изображения, больше чем hero-раздел, в случае, если автор использовал огромное изображение. Подробнее об этом вы можете прочитать в моей статье о минимальном размере содержимого в CSS-сетке.
Обратите внимание, как мы пришли к гораздо более чистому решению. Демо.
CSS display:contents
Я думаю, что это первый практический пример использования display:contents, который был для меня действительно полезный. Рассмотрим следующий пример, где у нас есть контент и изображение.
1 2 3 4 5 6 7 8 |
<div class="hero"> <div class="hero__content"> <h2><!-- Title --></h2> <p><!-- Desc --></p> <a href="#">Order now</a> </div> <img src="recipe.jpg" alt=""> </div> |
До сих пор ничего странного. На мобильных устройствах мы хотим добиться следующего макета.
Обратите внимание, что изображение теперь вставлено между заголовком и описанием. Как можно решить эту проблему? Поначалу может показаться, что разметка HTML должна быть изменена именно так.
1 2 3 4 5 6 7 8 |
<div class="hero"> <div class="hero__content"> <h2><!-- Title --></h2> <img src="recipe.jpg" alt=""> <p><!-- Desc --></p> <a href="#">Order now</a> </div> </div> |
На мобильном телефоне все может работать как положено. На десктопе нам нужно расположить img с правой стороны. И хотя это работает, но есть более простое решение display:contents. Вернемся к исходной разметке.
1 2 3 4 5 6 7 8 |
<div class="hero"> <div class="hero__content"> <h2><!-- Title --></h2> <p><!-- Desc --></p> <a href="#">Order now</a> </div> <img src="recipe.jpg" alt=""> </div> |
Обратите внимание, что содержимое завернуто внутрь .hero__content. Как мы можем сказать браузеру, что мы хотим, чтобы <h2>, <p> и <a> были наследниками того же родительского элемента, что и img? Что ж, с display:contents я могу это сделать:
1 2 3 |
.hero__content { display: contents; } |
Браузер проанализирует разметку следующим образом.
1 2 3 4 5 6 |
<div class="hero"> <h2><!-- Title --></h2> <p><!-- Desc --></p> <a href="#">Order now</a> <img src="recipe.jpg" alt=""> </div> |
Все, что нам нужно сделать в CSS, это следующее.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.hero { display: flex; flex-direction: column; } .hero__content { display: contents; } .hero h2, .hero img { order: -1; } |
При правильном использовании display:contents — это мощный метод для достижения того, что было невозможно несколько лет назад. Конечно, мы также хотим заняться стилем для десктопа.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@media (min-width: 750px) { .hero { flex-direction: row; } .hero__content { display: initial; } .hero h2, .hero img { order: initial; } } |
Изменение порядка элементов компонентов карты
В этом примере у нас есть вариант карты, в которой заголовок помещается вверху карты. Давайте посмотрим на разметку HTML.
1 2 3 4 5 6 7 8 |
<article class="card"> <img src="thumb.jpg" alt=""> <div class="card__content"> <h3>Title</h3> <p>Description</p> <p>Actions</p> </div> </article> |
Обратите внимание, что у нас есть два прямых дочерних элемента: ползунок и содержимое. Как с такой разметкой мы можем расположить заголовок h3 вверху? Мы можем сделать это с помощью абсолютного позиционирования.
1 2 3 4 5 6 7 8 9 10 11 |
.card { position: relative; padding-top: 3rem; /* Accommodate for the title space */ } .card h3 { position: absolute; left: 1rem; top: 1rem; } |
Однако это решение может не сработать, если заголовок слишком длинный.
Это происходит потому, что заголовок выходит за рамки обычного потока, поэтому браузер фактически не заботится о том, насколько он короткий или длинный. Мы можем найти гораздо лучшее решение используя display:contents:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.card { display: flex; flex-direction: column; padding: 1rem; } .card__content { display: contents; } .card h3 { order: -1; } |
Это позволит нам контролировать все дочерние элементы и иметь возможность записывать их по мере необходимости с помощью свойства order.
У нас все еще есть небольшая проблема, так как мы добавили padding:1 rem родительскому элементу. Изображение должно быть приклеено к краям. Вот как мы можем это исправить:
1 2 3 4 |
.card img { width: calc(100% + 2rem); margin-left: -1rem; } |
Центрирование
Когда дело доходит до центрирования, вы увидите множество шуток о том, как это сложно. В наши дни центрировать элемент намного проще, чем когда-либо.
Больше не нужно использовать преобразования position:absolute. Например, мы можем использовать margin:auto для центрирования гибкого элемента как по горизонтали, так и по вертикали.
1 2 3 4 5 6 7 |
.card { display: flex; } .card__image { margin: auto; } |
Соотношение сторон изображения
Новое свойство CSS aspect-ratio теперь поддерживается во всех основных браузерах. Раньше мы использовали отступы, чтобы соблюдать определенное соотношение сторон.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.card__thumb { position: relative; padding-top: 75%; } .card__thumb img { position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover; } |
С новым aspect-ratio это намного проще.
1 2 3 4 5 |
/* */ .card__thumb { position: relative; aspect-ratio: 4/3; } |
Иногда лучше использовать рosition:аbsolute
В этом примере у нас есть контент (аватар, имя и ссылка), перекрывающий обложку карты. У нас есть два варианта реализации этого:
Использовать position:absolute для обложки карты
Использовать отрицательное поле для содержимого
Давайте рассмотрим решения и посмотрим, какое из них больше подходит для этого варианта использования.
Использование абсолютного позиционирования
С помощью этого решения мы можем позиционировать прямоугольник абсолютно, а затем добавить padding:1rem к содержимому карты.
1 2 3 4 5 6 7 8 9 10 11 |
.card__cover { position: absolute; left: 0; top: 0; width: 100%; height: 50px; } .card__content { padding: 1rem; } |
Таким образом, когда покрытие карты убирается, нам не нужно редактировать или изменять CSS.
Использование отрицательных полей
В этом решении карта не будет позиционироваться абсолютно. Вместо этого контент будет иметь отрицательное поле сверху.
1 2 3 4 |
.card__content { padding: 1rem; margin-top: -1rem; } |
Хотя это решение работает при наличии покрытия карты, оно может вызвать проблемы, когда у нас есть вариант карты без покрытия.
Обратите внимание, как аватар находится за пределами своего родителя (карты). Чтобы исправить это, нам нужно изменить CSS и удалить отрицательное поле.
1 2 3 |
.card--no-cover .card__content { margin-top: 0; } |
В результате — использование position:absolute оказалось лучшим решением, так как избавит нас от написания дополнительных CSS. Надеюсь, вам понравилась статья. Спасибо за чтение!
Автор: Ahmad Shadeed
Источник: ishadeed.com
Редакция: Команда webformyself.
Читайте нас в Telegram, VK, Яндекс.Дзен