Точность вычисления процентных значений в CSS и Sass
Использовании процентных значений для указания размеров элементов может быть не таким простым делом, как могло показаться на первый взгляд. Каким образом браузер округляет вычисленное значение? В большую или меньшую сторону? И что делать, если из-за округления значений, в итоге, элементы попросту перестают соответствовать заданным размерам.
Поговорим об этих вещах, а также о возможных способах решения возникающих проблем.
Подготовка
Прежде чем начать разбор особенностей вычислений в CSS и Sass, возьмем небольшой кусочек кода, который отлично подойдет для наших целей:
.list-item {
float: left;
width: 33%;
}
Что не так с этим кодом?
Скорей всего, у вас возникнет резонный вопрос: -“А что же не так с этим кодом?”. На первый взгляд, там всего ничего – только три строчки кода, которые отвечают за построение трех-колоночной сетки. Довольно-таки просто, скажите вы.
Тем не менее, если произвести простое сложение ширины каждой из трех колонок, получается следующее: 33% + 33% + 33% равно 99%, но не 100%. В большинстве случаев, потеря точности в 1% не будет заметна, но при расположении подряд нескольких блоков – один процент точности имеет большое значение.
Например, 1% от 1400px
– это уже будет погрешность в 14px
, что является совершенно недопустимой величиной.
В таком случае, почему бы на просто не увеличить точность, задав значение ширины, например, с точностью до
сотой части? Мы могли бы уменьшить погрешность до 1.4px
, или даже до 0.14px
, что будет вполне подходящим
вариантом, как можно было бы предположить:
.list-item {
float: left;
width: 33.33%;
}
Этот код уже будет работать гораздо лучше, но все равно недостаточно хорошо.
Проблема заключается в том, что при ширине в 33.33% от 1400px
, браузер переведет это в 466.62px. Так как блоков
у нас три штуки, каждый блок в теории должен занять эту ширину. Но у каждого браузера свои алгоритмы расположения
блоков при дробных значениях ширины. Например, полученная нами дробная ширина в 466.62px, каким-то браузером
может округлиться до 467px. Проделав нехитрые вычисления: 467px * на три блока, получим общую ширину блоков в
1401px, что не поместится в наш контейнер размером в 1400px.
Браузеры не могут справиться с этой проблемой?
В этот момент можно было бы удивиться – неужели браузеры не в состоянии сделать так, чтобы это нормально работало? Суть в том, что в CSS спецификации не определено, что должны делать разработчики браузеров, чтобы решить вопрос с округлением процентных значений с плавающей точкой. И, если в спецификации не указаны детали реализации какой-либо особенности, то будьте уверены – каждый браузер сделает это по-своему.
Здесь уместно будет привести цитату из хорошей статьи на эту тему:
[…] в шести-колоночной сетке, каждая колонка будет равна 100% ÷ 6 = 16.666667% ширины. На примере контейнера шириной в 1000px (ширина выбрана для удобства вычислений), процентная ширина каждой колонки преобразуется в 166.66667 пикселей. Так как в спецификации нет руководства для действия в подобных случаях, разработчики браузеров придумывают свои правила. Если браузер будет округлять дробное значение к ближайшему пикселю, в нашем примере, мы получим 167 пикслей. Но это получается 167 x 6 = 1002 пикселей, и мы больше не имеем свободного пространства в контейнере для всех шести колонок. В другом случае, при окрулении браузером в меньшую сторону к 166 пикселям, мы получим лишние четыре пикселя, что не будет соответствовать размеру нашего контейнера для полного заполнения имеющегося пространства.
Это именно то, что случится. Старые версии Internet Explorer (в основном 6 и 7) округляют значение к ближайшему целому числу, что будет причиной поломки всей верстки.
Браузеры на движке Webkit округляют значение в меньшую сторону, что предотвращает катастрофические последствия для верстки, но оставляет дополнительное неиспользуемое пространство.
Opera (по крайней мере в старых версиях движка) производить какие-то совершенно странные манипуляции, которые даже попытаться объяснить не представляется возможным.
Но опять же, нет описания правил поведения в спецификации, поэтому не стоит винить во всем только лишь разработчиков браузеров.
Как бы то ни было, все вышесказанное приводит к путанице, и мы вернемся к этому вопросу в заключительном выводе к этой статье.
Что на счет Sass?
Как известно, Sass поддерживает математические операции. Это одна из множества возможностей Sass, из-за которой стоит использовать этот CSS-препроцессор, например, для построения grid-систем. Все, что мы должны сделать, так это сказать Sass, что мы хотели бы разделить ширину нашего контейнера на три равные части:
.list-item {
float: left;
width: (100% / 3);
}
Также, мы могли бы использовать для этих целей percentage()
функцию:
.list-item {
float: left;
width: percentage(1 / 3);
}
Sass, в обоих случаях реализации на Ruby или LibSass, имеет значение точности вычислений равное пяти знакам после запятой. Это является некоторой проблемой, из-за малого значения точности и сложности с её переопределением – точность в десять знаков была бы в самый раз.
Следующий код является результатом работы Sass:
.list-item {
float: left;
width: 33.33333%;
}
Это не решает нашу проблему с браузерами, но создание таблиц стилей в таком случае становится значительно проще. Нам нет необходимости производить собственноручно какие-либо вычисления, также мы делаем код более удобным для чтения и дальнейших изменений, благодаря тому, что непосредственно указали производимые действия.
Лучшее из Sass и CSS
Только что мы узнали, что хорошей идеей будет позволить Sass производить за нас все вычисления, чем жестко
указывать какие-либо значения. Теперь, комбинируя полученные знания, самым лучшим вариантом решения возникших
проблем – это дать возможность браузеру решить созданную им же проблему. Для этого используем CSS-функцию calc()
:
.list-item {
float: left;
width: calc(100% / 3);
}
Этот кусочек кода не будет претерпевать каких-то ни было изменений, – он будет работать так, как должен по
спецификации CSS. Точно не известно, обрабатывает ли браузер значения функции calc()
по тем же алгоритмам, что и
без неё. Предположительно, некоторые браузеры добавляют дополнительные пиксели при рендере для выравнивания
ширины блоков.
Для тех браузеров, которые не поддерживают функцию
calc()
– это в основном
Internet Explorer 8 и Opera Mini, мы можем поместить выражение Sass, результатом работы которого будет
статическое значение ширины, прямо рядом с calc()
. Такой вариант является наиболее подходящим для нашего случая, и
содержит в себе лучшее из CSS и Sass:
.list-item {
float: left;
width: (100% / 3);
width: calc(100% / 3);
}
Заключение
Давайте подведем небольшое резюме из вышесказанного.
Верстка, основанная на процентных значениях, представляет из себя довольно таки сложную реализацию из-за того, что отсутствует спецификация по этому вопросу, и каждый браузер рендерит такую верстку по своему усмотрению.
Далее, установка жестко прописанных значений вычислений – это не слишком хорошая идея. Гораздо лучшим вариантом будет использование Sass для вычисления приближенных значений с точностью до пятого знака после запятой.
Ну а самым лучшим вариантом будет использование возможностей браузера для вычисления значений с помощью CSS-функции calc()
.
На этом все. Надеюсь, это небольшое напоминание об особенностях вычисления значений в CSS и Sass будет полезным.
При создании статьи были использованы следующие источники: