Когда мы проходим собеседования, нам часто задают вопросы по верстке. Зачастую они сводятся к перечислению значений свойства display или способов центрирования элементов. Меня это откровенно достало, и мне захотелось придумать свои задачи, с помощью которых можно проверить знания верстальщика, а не то, как он зазубрил значения свойств.
Описание задачи
Часто, когда читаю статьи в интернете, я встречаю следующую проблему: текст начинает прыгать при загрузке изображения.
Одним из решений этой проблемы является добавление блока-заглушки под изображение, у которого соотношение ширины и высоты будет повторять соотношение сторон изображения.
Реализацию такого блока я начну с разметки:
<div class="post">
<img src="example.jpg" class="post__img" alt="Черный кот в шляпе">
</div>
Чтобы задать размеры для блока-заглушки, нужно узнать соотношение сторон изображения. Для примера я буду использовать изображение размером 1920x1080px.
Но если выводить изображение такого размера, то на большинстве экранов оно будет отображаться с горизонтальной прокруткой. Поэтому элементу .post я задам максимальную ширину 640px, а ширина изображения будет подстраиваться под нее.
.post {
max-width: 640px;
}
.post__img {
max-width: 100%;
}
Добавление блока-заглушки на страницу я сделаю при помощи псевдоэлемента before. Также нужно не забыть расположить изображение поверх него.
.post {
max-width: 640px;
position: relative;
}
.post::before {
content: "";
}
.post__img {
max-width: 100%;
position: absolute;
top: 0;
left: 0;
}
Теперь нужно, чтобы ширина блока-заглушки повторяла ширину изображения. Ранее я сделал так, чтобы изображение занимало 100% ширины родительского элемента .post. То же самое сделаю для псевдоэлемента before, добавив display: block к нему.
.post {
max-width: 640px;
position: relative;
}
.post::before {
content: "";
display: block;
}
.post__img {
max-width: 100%;
position: absolute;
top: 0;
left: 0;
}
Друзья, на этом я закончил свою часть работы, и осталась ваша. Вам требуется рассчитать высоту псевдоэлемента before.
Для этого нужно использовать свойство padding-top со значением в процентах. Чтобы сделать это правильно, нужно знать особенность расчета процентов у свойства padding-top. А также помнить, что размеры изображения 1920x1080px.
.post {
max-width: 640px;
position: relative;
}
.post::before {
content: "";
display: block;
/* здесь будет свойство padding-top со значением в % */
}
.post__img {
max-width: 100%;
position: absolute;
top: 0;
left: 0;
}
Я надеюсь, что вы попробуйте самостоятельно решить эту задачу. Но если возникнут трудности, ниже есть мое решение.
Решение
У свойств padding-top и padding-bottom есть одна очень полезная особенность. Если для них задать значения в процентах, то они будут рассчитываться от ширины родительского блока.
Соответственно, зная это и помня размеры изображения (1920x1080px), можно рассчитать padding-top для блока-заглушки с помощью пропорции:
padding-top = (Ви * 100%) / Ши = (1080 * 100% ) / 1920 = 56.25%
где Ши и Ви — ширина и высота изображения.
Осталось добавить значение для padding-top:
.post {
max-width: 640px;
position: relative;
}
.post::before {
content: "";
display: block;
padding-top: 56.25%;
}
.post__img {
max-width: 100%;
position: absolute;
top: 0;
left: 0;
}
Комментарии (50)
Suliman
20.12.2018 10:25Если вы располагаете картинку абсолютом, то зачем вообще использовать :before? давайте паддинг сразу для .post
melnik909 Автор
20.12.2018 10:27Можно, но тогда надо обвернуть div.post еще одним div'ом и ему ширину задать, чтобы проценты корректно рассчитывались.
Suliman
20.12.2018 10:48Если рассматривать реальный вариант который бы использовался в реальном проекте, то здесь много чего добавлять еще нужно, например что если картинка должна быть ссылкой, что если .post будет иметь padding, а картинка у вас имеет top:0; и т.д. Но в общем идея не плохая.
Kurienko
20.12.2018 12:02почему нельзя задать сразу размер в картинке?
браузер сразу забронирует место под картинку с указаними размерами и все ето не будет прыгать…melnik909 Автор
20.12.2018 12:03+1Какие единицы измерения вы будете использовать для этого?
Klenov_s
20.12.2018 12:10Пикселы, конечно.
melnik909 Автор
20.12.2018 13:19А вам надо изображение, которое адаптируется на разных экранах. Что тогда?
NickyX3
21.12.2018 08:29для этого есть vw/vh, если привязывать все ширины к vw, то в случае известного соотношения сторон можно тупо сделать, например min-height: calc( (100vw/16)*9 );
calc вообще удобная штукаdom1n1k
21.12.2018 10:09В реальности «привязывать все ширины к vw» — сомнительная практика (очень мягко говоря).
NickyX3
21.12.2018 10:22Почему же?
Вот есть у вас 100% ширина и вам нужны картинки в три колонки.
Что сомнительного в
width: calc( (100vw — 10px)/3 );
min-height: calc( ((100vw — 10px)/48)*9 );
????dom1n1k
21.12.2018 10:591. Поизучайте спецификации, всегда ли равны 100% и 100vw. Или минус 10px это и есть магическая константа для борьбы с расхождениями?
2. Картинка чаще должна быть привязана не к ширине вьюпорта, а вписываться в родительский контейнер (потому что он часть сетки), о размере которого она ничего не знает.NickyX3
21.12.2018 11:201. Я разве говорил, что 100vw == 100%? 10px это условный размер скрола, чтоб не менялась ширина, если страница будет длинная.
2. Картинка и так 100% ширины родителя, а вот он и привязан к ширине вьюпорта.
К сожалению — ширина и высота вьюпорта сейчас это единственные абсолютные величины, к которым можно привязаться для расчета соотношения сторон блока под картинку. В отличие от процентов.
Сомнительные «хаки» выравнивания по вертикали типа процентных padding, или line-height тоже так себе практика, ибо они явно для этого не предусмотреныdom1n1k
21.12.2018 12:27Вот именно, что «условный». В зависимости от операционной системы и пользовательских настроек, он везде будет разным. От нуля до нескольких десятков px.
Эта особенность очень сильно обесценивает единицы вьюпорта, хотя многие разрабы относятся к ним с эйфорией.
Они удобны в случаях, когда не нужна особая точность. Но в большинстве ситуаций это очень скользкие единицы и обращаться с ними нужно крайне внимательно и осторожно.
Они как рыба фугу — круто, но предательски опасно, а потому её могут готовить только очень квалифицированные повара.
«Хак» с паддингом — не совсем хак, хотя на первый взгляд и выглядит таковым. Не хак, потому что он основан на документированном поведении из спецификации. Это не какая-то там багофича, которая может исчезнуть в новой версии браузера. Это работает так, как и должно работать.NickyX3
21.12.2018 14:22это очень скользкие единицы и обращаться с ними нужно крайне внимательно и осторожно
Да никто не спорит, но обычно подавляющее большинство сайтов все же фиксировано по ширине в каких-то пределах, возможно даже в нескольких в зависимости от ширины и таки имеют поля по бокам, поэтому можно вычесть магические пикселы по максимуму и отталкиваясь от этого размера уже считать все остальное резиновое.
Для тех сайтов, где контент на полную ширину, достаточно 100vw, и оно будет типа full-screen, что и для мобильных подходит тоже.
основан на документированном поведении из спецификации
Документированное поведение не означает, что, исходя из простейшей логики, padding и line-height не предназначались для выравнивания блоков по вертикали или расчета размера по минимальной высоте. Для первого достаточно flex-box, для второго min/max-height при фиксированной ширине. Для сохранения соотношения при резиновой ширине считаю calc относительно vw вполне работоспособный вариант на «чистом» cssdom1n1k
21.12.2018 16:01> большинство сайтов все же фиксировано по ширине в каких-то пределах [...] и таки имеют поля по бокам
Если сайт фиксирован по ширине, то тем более не получится привязывать ширины элементов к ширине вьюпорта.
> где контент на полную ширину, достаточно 100vw, и оно будет типа full-screen
Угу, с горизонтальным скроллом. Вы же не будете всерьез предлагать overflow: hidden? Я надеюсь.
> для второго min/max-height при фиксированной ширине
Если ширина фиксирована, проблемы нет вообще — там просто указывается height и всё. Вся суть задачи обсуждаемой именно в том, чтобы сделать резину с сохранением пропорций.
> считаю calc относительно vw вполне работоспособный вариант
Ну значит у нас разные представления о качестве верстки.NickyX3
21.12.2018 16:14Если сайт фиксирован по ширине, то тем более не получится привязывать ширины элементов к ширине вьюпорта.
Легко, к примеру ширина сайта привязана к ширине вьюпорта в зависимости от его ширины. По разному.
К примеру
При ширине вьюпорта овер 1280, ширина контента 70vw, при меньшей — 90vw, что не так? Я же описал случай, фиксирована в зависимости от максимальной, привязать к vw легко, media query еще не отменили же.
Ну значит у нас разные представления о качестве верстки.
У нас разные представления о качестве дизайна, и способах решенияdom1n1k
21.12.2018 17:20> При ширине вьюпорта овер 1280, ширина контента 70vw, при меньшей — 90vw, что не так?
Не так то, что при переходе через брейкпойнт в большую сторону ширина контента будет наоборот уменьшаться, что противоестественно.
mobi
20.12.2018 13:31А не проще тогда «по старинке» задавать размеры изображения в атрибутах width и height, а масштабировать под ширину контейнера через
?img { max-width: 100%; height: auto; }
melnik909 Автор
20.12.2018 14:35Попробуйте и узнаете, что нет
mobi
20.12.2018 17:04Проверил, действительно «нет». Но я зато придумал, как можно выкрутиться, если по какой-то причине нет возможности обернуть изображение в слой: jsfiddle.net/r5bnjm87 (вдруг, кому пригодится).
mobi
20.12.2018 17:25Хотя, нет, эта идея пока еще сырая. Пока у img не появится shadow dom, к нему нельзя применить ::before.
lifecom
20.12.2018 16:19Я так понимаю, нам необходимы даже не размеры, а соотношение сторон.
А js-ом нельзя получить все размеры изображений на странице ДО их загрузки?
Вообще было бы полезно «дёргать» из изображений до их загрузки какие-то данные, по типу EXIFAlexufo
20.12.2018 17:32если размеры изображения известны ДО загрузки на станице т.е на сервере, сервером и надо прописывать, а js приплетать для верстки это последнее дело в каком нибудь полифиле.
Alexufo
20.12.2018 17:31Подождите, если мы говорим про padding-top в процентах, то картинка автоматом идет в бекграунд. К черту img вам этот?
Картинку на бекграунд, блоку padding-top:% в требуемых пропорциях. Вообще у AirBnB мне нравится как сделано, но меня вымораживает их бесполезная иерархическая вложенность, которая абсолютно ничем не оправдана.melnik909 Автор
20.12.2018 20:05контентные изображения не надо фоном делать)
Alexufo
20.12.2018 22:31Ну вот AirBnb это не волнует, у них правда сайт на телефоне моем тормозит дичайшим образом.
Очень странно, что вы ни слова не написали об этом
developer.mozilla.org/ru/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images
Вордпресс во всю генерит тьму ресайзов для этого, но внедрение этого я вижу не у всех.
Alexufo
22.12.2018 01:45допустим, к черту все минусы и правила, тогда бы вы как логику выстраивали? Этот способ бы вас успокоил?
pepelsbey
20.12.2018 22:15контентные изображения не надо фоном делать
Они хуже грузятся, бесполезны в режимах чтения, неудобны для скринридеров, не индексируются поисковиками, …продолжать?
Alexufo
21.12.2018 00:28В городе высокие бордюры, людям с сумками на колесиках неудобно их преодолевать.И вы общаетесь не к господам, отвечающим за бордюры, а к производителям сумок на колесиках: делайте размеры колес больше, вы делаете сумки не так как мы от вас ожидали.
Попадаются забавные вещи, как отсутствие схожих свойств у близких по духу элементов.
В html5 добавили по srcset и тегу img и в background. И тег picture появился, а делать background-size: cover он так и не научился.Aingis
21.12.2018 12:13И тег
А что случилось сpicture
появился, а делатьbackground-size: cover
он так и не научился.object-fit
? Перестало работать?
lipton_ice_tea
20.12.2018 18:45Можно лучше) Не будут прыгать изображения. Картинки будут появляться плавно, а не прогружаться вертикально. Будет ленивая загрузка (по желанию). И любое соотношение сторон.
- Получаем размеры картинок на сервере и прописываем их в атрибуты width и height у img.
- Меняем src на data-src
- В css пишем для img max-width:100% (ну или сколько угодно)
- Добавляем скрипт js, который сравнивает фактическую ширину картинки с шириной в атрибуте width, и обновляет height
- Дальше в js дергаем значение data-src и грузим в фоне. Когда картинка загружена — меняем data-src на src. (Это можно делать либо сразу, либо после загрузки основного контента будет ленивая загрузка)
- В css либо пишем img[src], либо класс с height:auto (лучше класс)
melnik909 Автор
20.12.2018 20:06а для чего нужно height: auto? Оно же вроде так по умолчанию
lipton_ice_tea
21.12.2018 07:07Тк атрибут height у img перекрывает свойство по умолчанию. Поэтому что бы перекрыть атрибут, мы и добавляем правило в css.
Но как альтернатива — можно просто в js удалять атрибут height у img — эффект тот же
Alexufo
21.12.2018 00:04Как сделано у медиума
контейнер имеет физические данные картинки max-width: 542px; max-height: 826px;
— плейсхолдер так же заранее расчитан padding-bottom: 152.4%;
— врапер с absolute top:0;left:0;width:100%;height:100%
—— img c src 30px превьюшки
—— canvas блюрит превьюшку
—— оригинальному img вставляется путь из data-src в src, пошла реальная загрузка файла
—— ноускрипт
——— оригинальный img c src.
Мне кажется сравнения размеров на клиенте нехорошая идея, если все размеры кешируются сервером, точнее хранятся там же где и хранится подпись к картинке. Один перец это довольно важная инфа для хранения, для той же статистики.lipton_ice_tea
21.12.2018 07:17- В src, действительно, удобно размещать превью и блюрить на время загрузки оригинального изображения.
- По поводу сравнения размеров на клиенте — если такой вариант не нравится — то можно все картинки оборачивать в контейнер, padding-top которого рассчитывать на сервере, в зависимости от соотношения сторон загружаемого изображения. Саму картинку в абсолют.
- Если размер изображения НЕ хранится на сервере, его можно получить во время обращения к странице (операция довольно быстрая)
dom1n1k
21.12.2018 10:12Это пример «горя от ума». Не нужно скриптом плавно, пусть прогружаются вертикально и автоматически.
pepelsbey
Новичкам конечно будет полезно узнать про этот трюк, но причина обозначена неудачно. Беда в том, что если картинка не 16:9, то от прыжков мы всё равно никуда не денемся. Только если договоримся прописывать размеры в атрибутах на сервере.
melnik909 Автор
Можно:
— договориться о системе классов и через них прописать пропорции
— использовать CSS Custom Properties в атрибуте style у div'а
— добавить к div'у атрибут style и в нем указать padding (как делает Instagram)
Это уже детали реализации. Но многие специалисты не знают, что такое вообще возможно.
Against-vegetables
У картинок, если они не пользовательские, должны быть стандартизированы размеры. А вот для ютубовского айфрейма этим решением приходится пользоваться регулярно.