en.javascript.info/12-css-for-js/2-css-units/article.md
2015-02-27 13:21:58 +03:00

17 KiB
Raw Blame History

Единицы измерения: "px", "em", "rem", "%" и другие

В этом очерке я постараюсь не только рассказать о различных единицах измерения, но и построить общую картину -- что и когда выбирать.

[cut]

Абсолютные: px

Пиксель px -- это самая базовая, абсолютная и окончательная единица измерения.

Количество пикселей задаётся в настроках [разрешения экрана](http://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_(%D0%BA%D0%BE%D0%BC%D0%BF%D1%8C%D1%8E%D1%82%D0%B5%D1%80%D0%BD%D0%B0%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D0%BA%D0%B0), один px -- это как раз один такой пиксель на экране. Все значения браузер в итоге пересчитает в пиксели.

Пиксели могут быть дробными, например размер можно задать в 16.5px. Это совершенно нормально, браузер сам использует дробные пиксели для внутренних вычислений. К примеру, есть элемент шириной в 100px, его нужно разделить на три части -- волей-неволей появляются 33.333...px.

Для мобильных устройств, у которых много пикселей на экране, но сам экран маленький, чтобы обеспечить читаемость, браузер автоматически применяет масштабирование. При окончательном отображении дробные пиксели, конечно же, округляются и становятся целыми.

[compare] +Главное достоинство пикселя -- чёткость и понятность -Другие единицы измерения -- в некотором смысле "мощнее", они являются относительными и позволяют устанавливать соотношения между различными размерами [/compare]

[warn header="Давно на свалке: mm, cm, pt, pc"]

Существуют также "производные" от пикселя единицы измерения: mm, cm, pt и pc, но они давно отправились на свалку истории.

Вот, если интересно, их значения:

  • `1mm` (мм) = `3.8px`
  • `1cm` (см) = `38px`
  • `1pt` (типографский пункт) = `4/3 px`
  • `1pc` (типографская пика) = `16px`
Так как браузер пересчитывает эти значения в пиксели, то смысла в их употреблении нет. [/warn]

[smart header="Почему сантиметре cm содержится ровно 38 пикселей?"] В реальной жизни сантиметр -- это такой отрезок, эталон длины. А пиксель может быть разным, в зависимости от экрана.

Но в формулах выше под пикселем понимается "сферический пиксель в вакууме", точка на "стандартизованном экране", характеристики которого описаны в спецификации.

Поэтому ни о каком соответствии cm реальному сантиметру здесь нет и речи. Полностью синтетическая и ненужная единица измерения. [/smart]

Относительно шрифта: em

1em -- текущий размер шрифта.

Можно брать любые пропорции от текущего шрифта: 2em, 0.5em и т.п.

Размеры в em -- относительные, они определяются по текущему контексту.

Например, давайте сравним px с em на таком примере:

<!--+ autorun height=80 -->
<div style="font-size:24px">
  Страусы
  <div style="font-size:24px">Живут также в Африке</div>
</div>

24 пикселей -- и в Африке 24 пикселей, поэтому две строчки выше одинаковы.

А вот аналогичный пример с em вместо px:

<!--+ autorun height=120 -->
<div style="font-size:1.5em">
  Страусы
  <div style="font-size:1.5em">Живут также в Африке</div>
</div>

Так как значение в em высчитывается относительно текущего шрифта, то вторая строка в 1.5 раза больше, чем первая.

Выходит, задавая размеры в em, мы задаём зависимость элементов от стиля родителя. Хочется увеличить или уменьшить всё вместе -- легко, просто поправим один стиль.

[smart header="Также относительно шрифта: ex, ch"] В спецификации указаны также единицы ex и ch ), которые означают размер в зависимости от таких характеристик шрифта как размер символа "x" и размер символа "0".

Эти размеры присутствуют всегда, даже если по коду этих символов в шрифте находятся другие значения, а не именно буква "x" и ноль "0".

Эти единицы используются чрезвычайно редко, так как "размер шрифта" em обычно вполне подходит. [/smart]

Проценты %

Проценты %, как и em -- относительные единицы.

Вот пример с %, он выглядит в точности так же, как с em:

<!--+ autorun height=120 -->
<div style="font-size:150%">
  Страусы
  <div style="font-size:150%">Живут также в Африке</div>
</div>

Когда мы говорим "процент", то возникает вопрос -- "Процент от чего?"

Как правило, процент будет от значения свойства родителя с тем же названием, но не всегда.

Это очень важная особенность процентов, про которую, увы, часто забывают.

Отличный источник информации по этой теме -- стандарт, Visual formatting model details.

Некоторые примеры:

  • При установке свойства `margin-left` в `%`, процент берётся от *ширины* родительского блока (а вовсе не от его `margin-left`!)
  • При установке свойства `line-height` в `%`, процент берётся от текущего *размера шрифта* (а вовсе не от текущего `line-height`!)
  • Для `width/height` при `position:fixed`, процент берётся от ширины/высоты *окна* (а не родителя и не документа).
  • Кроме того, иногда `%` требует соблюдения дополнительных условий, за примером -- обратитесь к статье [](/height-percent).
  • Детали по `line-height` и размеру шрифта вы также можете найти в статье [](/font-size-line-height).

Микс px и em: rem

Итак, что у нас есть:

  • `px` -- абсолютные, чёткие, понятные, не зависящие ни от чего.
  • `em` -- относительно размера шрифта.
  • `%` -- относительно такого же свойства родителя (а может и не родителя, а может и не такого же -- см. оговорки выше).

Может быть, пора уже остановиться, может этого достаточно?

Э-э, нет! Не все вещи делаются удобно.

Вернёмся к теме шрифтов. Бывают задачи, когда мы хотим сделать на странице большие кнопки "Шрифт больше" и "Шрифт меньше". При нажатии на них будет срабатывать JavaScript, который будет увеличивать или уменьшать шрифт.

Вообще-то это можно сделать без JavaScript, в браузере обычно есть горячие клавиши для масштабирования вроде [key Ctrl++], но они работают слишком тупо -- берут и увеличивают всю страницу, вместе с изображениями и другими элементами, которые масштабировать как раз не надо. А надо увеличить, к примеру, только шрифт, потому что посетитель хочет комфортнее читать. И увеличенный шрифт по-новому обтечёт элементы страницы.

Какую единицу использовать для задания шрифтов? Лучше бы не px, ведь такие значения абслютны, если менять, то везде, вполне возможна ситуация, когда мы в одном правиле размер поменяли, а другое забыли.

Следующие кандидаты -- em и %.

Разницы между ними здесь нет, так как при задании font-size в процентах, эти проценты берутся от font-size родителя, то есть ведут себя так же, как и em.

Вроде бы, использовать можно, однако есть проблема.

Посмотрите на пример с вложенными <li>:

<!--+ run autorun -->
<style>
  li {
    font-size: 0.8em;
  }
</style>

<ul>
<li>Собака
  <ul>
  <li>бывает
    <ul>
    <li>кусачей
      <ul>
      <li>только
        <ul>
        <li>от жизни
          <ul>
          <li>собачей</li>
          </ul>
        </li>
        </ul>
      </li>
      </ul>
    </li>
    </ul>
  </li>
  </ul>
</li>
</ul>

Проблема очевидна. Хотели, как лучше, а получилось... Мелковато. Каждый <li> получил размер шрифта 0.8 от внешнего, это не совсем то, чего мы бы здесь хотели.

Время появиться единице rem, которая, можно сказать, придумана для таких случаев!

Единица rem задаёт размер относительно размера шрифта элемента <html>.

Как правило, браузеры ставят этому элементу некоторый "разумный" (reasonable) размер по-умолчанию, который мы, конечно, можем переопределить и использовать rem для задания шрифтов внутри:

<!--+ run height=400 autorun -->
<style>
*!*
  html {
    font-size: 14px;
  }
  li {
    font-size: 0.8rem;
  }
*/!*
</style>

<div><button id="up">Кликните, чтобы увеличить размер шрифта</button></div>

<img src="https://js.cx/clipart/angry_dog.png">

<ul>
<li>Собака
  <ul>
  <li>бывает
    <ul>
    <li>кусачей
      <ul>
      <li>только
        <ul>
        <li>от жизни
          <ul>
          <li>собачей</li>
          </ul>
        </li>
        </ul>
      </li>
      </ul>
    </li>
    </ul>
  </li>
  </ul>
</li>
</ul>

<script>
var html = document.documentElement;
up.onclick = function() {
  // при помощи JS увеличить размер шрифта html на 2px
  html.style.fontSize = parseInt(getComputedStyle(html, '').fontSize) + 2 + 'px';
};
</script>

Получилось удобное масштабирование для шрифтов, не влияющее на другие элементы.

Можно воспринимать rem как остановку посередине между em и px, потому что элементы в rem не зависят друг от друга и от контекста -- и этим похожи на px, а с другой стороны они все заданы относительно размера шрифта <html>.

[warn header="Не поддерживается в IE8-"] Единица rem не поддерживается в IE8-. Для этих браузеров, которых становится всё меньше, есть два выхода:

  1. Либо использовать `em` и правила, страхующие от вложенности, вроде:
    li { font-size: 0.8em }
    li li { font-size: 0.8em }
    li li li { font-size: 0.8em }
    
  2. Либо сделать два правила в стилях: первое указывает размер в `px` (для IE8-), а второе -- в `rem` (переопределит первое в современных браузерах) и отключить маштабирование шрифтов для IE8-. Это проще.
[/warn]

Относительно экрана: vw, vh, vmin, vmax

Во всех современных браузерах, исключая IE8-, поддерживаются новые единицы из черновика стандарта CSS Values and Units 3:

  • `vw` -- 1% ширины окна
  • `vh` -- 1% высоты окна
  • `vmin` -- наименьшее из (`vw`, `vh`), в IE9 обозначается `vm`
  • `vmax` -- наибольшее из (`vw`, `vh`)

Эти значения были созданы, в первую очередь, для поддержки мобильных устройств.

Их основное преимущество -- в том, что любые размеры, которые в них заданы, автоматически масштабируются при изменении размеров окна.

Ниже написан текст с размером 5vh:

Страусы кусачими не бывают.

Вы сможете легко увидеть, как работает vh, если поменяете размер окна браузера. Текст будет расти/уменьшаться.

Итого

Мы рассмотрели единицы измерения:

  • `px` -- абсолютные пиксели, к которым привязаны и потому не нужны `mm`, `cm`, `pt` и `pc`. Используется для максимально конкретного и точного задания размеров.
  • `em` -- задаёт размер относительно шрифта родителя, можно относительно конкретных символов: `"x"`(`ex`) и `"0"`(`ch`), используется там, где нужно упростить масштабирование компоненты.
  • `rem` -- задаёт размер относительно шрифта `<html>`, используется для удобства глобального масштабирования: элементы которые планируется масштабировать, задаются в `rem`, а JS меняет шрифт у `<html>`.
  • `%` -- относительно такого же свойства родителя (как правило, но не всегда), используется для ширин, высот и так далее, без него никуда, но надо знать, относительно чего он считает проценты.
  • `vw`, `vh`, `vmin`, `vmax` -- относительно размера экрана.

Здесь я постарался разобрать их применение и преимущества для решения конкретных задач, но вы, конечно, сможете использовать их для других ситуаций.