Frontender Magazine

Центрирование в CSS: полное руководство

Центрирование элементов — самая популярная причина для жалоб на CSS. «Ну почему нельзя было сделать всё попроще?» — возмущаются все кому не лень. Думаю, проблема не в том, что это сложно сделать, а в том, что способов центрирования элементов такое множество, что бывает трудно выбрать подходящий.

Давайте построим древовидную схему для принятия решения и будем надеяться, что это всё немного упростит.

Мне нужно отцентрировать элемент...

По горизонтали

Является ли элемент строчным или строчно-* (как текст или ссылки)?

Строчные элементы внутри родительского блочного можно центрировать так:

.center-children {
  text-align: center;
}

Это сработает для элементов с типом отображения inline, inline-block, inline-table, inline-flex и т.д.

Является ли элемент блочным?

Блочный элемент можно центрировать, указав для margin-left и margin-right значение auto (а также прописав для него конкретный width, иначе он займёт всю ширину родительского контейнера и не будет нуждаться в центрировании). Для этого часто используют сокращённую запись:

.center-me {
  margin: 0 auto;
}

Способ работает независимо от ширины центрируемого блочного элемента и его родителя.

Обратите внимание, что заставить элемент плавать по центру с помощью float нельзя. Хотя есть одна хитрость.

Вам нужно центрировать несколько блочных элементов?

Если вы имеете дело с двумя и больше блочными элементами, размещёнными в ряд, которые нужно центрировать по горизонтали, возможно, вам потребуется изменить тип display. Вот что произойдёт, если указать для них тип отображения inline-block или применить flexbox:

В том же случае, если у вас несколько блочных элементов размещены друг над другом, вполне сработает приём с автоматическими отступами:

По вертикали

С вертикальным центрированием в CSS все немного сложнее:

Это элемент строчного типа или строчно-* (как текст или гиперссылки)?

Он помещается в одну строку?

Иногда строчные/текстовые элементы могут выглядеть отцентрированными по вертикали только потому, что у них одинаковые верхнее и нижнее поля (padding).

.link {
  padding-top: 30px;
  padding-bottom: 30px;
}

Если использование полей по какой-либо причине невозможно, а вы хотите отцентрировать текст, который точно не будет переноситься в следующую строку, можно использовать хитрость с установкой line-height равной высоте элемента, это отцентрирует текст:

.center-text-trick {
  height: 100px;
  line-height: 100px;
  white-space: nowrap;
}

Элемент занимает несколько строк?

Центрирование нескольких строчек текста также можно сделать с помощью установки одинаковых верхнего и нижнего полей, однако, если этот вариант вам не подходит, можно сделать элемент, в который помещён текст, ячейкой таблицы — буквально или же просто её имитацией на CSS. За центрирование в этом случае отвечает свойство vertical-align, несмотря на то, что обычно он просто выравниванивает по горизонтали элементы в ряду. (Более подробно об этом.)

Если вы имеете дело с некоей имитацией таблицы, может быть, стоит использовать flexbox? Один дочерний flex-элемент можно довольно легко отцентрировать во flex-родителе.

.flex-center-vertically {
  display: flex;
  justify-content: center;
  flex-direction: column;
  height: 400px;
}

Помните, что этот способ подходит только если у родительского контейнера указана конкретная ширина (px, % и т.д.), поэтому здесь у контейнера есть высота.

Если оба этих способа не подходят, можно использовать приём «элемент-призрак», при котором в контейнер помещается псевдоэлемент с максимальной высотой, и текст выравнивается по горизонтали по этому элементу:

.ghost-center {
  position: relative;
}
.ghost-center::before {
  content: " ";
  display: inline-block;
  height: 100%;
  width: 1%;
  vertical-align: middle;
}
.ghost-center p {
  display: inline-block;
  vertical-align: middle;
}

Это блочный элемент?

Вам известна высота элемента?

Обычно когда речь идёт о веб-странице, высота может быть неизвестна по многим причинам: если изменяется ширина элементы, перераспределение текста может изменить высоту. Вариации в стилизации текста могут изменить высоту. Изменение количества текста может изменить высоту элемента. Элементы с фиксированным соотношением сторон, например, изображения, могут изменить высоту при масштабировании, и так далее.

Но, если вам известна высота, вертикальное центрирование можно сделать так:

.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 50%;
  height: 100px;
  margin-top: -50px; /* учитывайте поля и границы, если не используете box-sizing: border-box; */
}

Вам неизвестна высота элемента?

Его все-таки можно центрировать, подняв вверх на половину его высоты после того, как сдвинули его наполовину вниз:

.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}

Вы можете использовать flexbox?

Не удивительно, что всё это можно сделать значительно проще с помощью flexbox.

.parent {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

И по горизонтали, и по вертикали

Описанные выше приёмы можно комбинировать как угодно, чтобы получить идеально отцентрированные элементы. Но, по моему опыту, обычно всё сводится к трём вариантам:

У элемента фиксированная высота и ширина?

Использование отступов с отрицательными значениями, равными половине высоты и ширины элемента после того, как для него было прописано абсолютное позиционирование в точке 50%/50%, отцентрирует элемент внутри родителя. Кроме того, способ хорошо поддерживается браузерами:

.parent {
  position: relative;
}

.child {
  width: 300px;
  height: 100px;
  padding: 20px;

  position: absolute;
  top: 50%;
  left: 50%;

  margin: -70px 0 0 -170px;
}

Высота и ширина элемента неизвестны?

Если вам не известны ширина или высота, для центрирования можно использовать свойство transform и отрицательное значение translate по 50% в обеих направлениях (оно вычисляется от текущей ширины/высоты элемента):

.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

Вы можете использовать flexbox?

Чтобы выполнить центрирование в обеих направлениях с помощью flexbox, нужно использовать два центрирующих свойства:

.parent {
  display: flex;
  justify-content: center;
  align-items: center;
}

Заключение

С помощью CSS вы можете отцентрировать всё что угодно.

Если вы заметили ошибку, вы всегда можете отредактировать статью, создать issue или просто написать об этом Антону Немцеву в skype ravencry.

Chris Coyier
Наталья Фадеева
Переводчик:
Наталья Фадеева
вКонтакте:
natatik_l
Twitter:
@very_busy_girl
GitHub:
NatalieF

Комментарии (10 комментариев, если быть точным)

Автар пользователя
gwer

Если переводить вплоть до упоминания схемы («Давайте построим древовидную схему для принятия решения»), то и саму схему неплохо было бы либо повторять, либо где-то графически демонстрировать.

Автар пользователя
nex2hex

Сейчас уже кроме флекбоксов остальное использовать неактуально

Автар пользователя
theexplay

nex2hex, Вы хотите сказать, что частичная поддержка ИЕ с 10, это нормально? И можно забыть о старых способах? http://caniuse.com/#search=flex

Автар пользователя
lekzd

Вариант с transform: translate имеет существенный минус - иногда элемент смещается на дробное количество пикселей, отчего выглядит слегка размытым

Автар пользователя
Photon79

theexplay, а что, поддержка IE жизненно необходима? Большинство людей пользуются нормальнымы браузерами, поэтому смело можно использовать flexbox

Автар пользователя
theexplay

Photon79, я бы хотел с вами согласиться, но мне часто при разработке ставят условие поддерживать старые браузеры, самый минимум ие9+

Автар пользователя
Riim

В чём смысл использовать "flex-direction: column;" вместо логичного в таких случаях "align-items: center;"?

Автар пользователя
eplaksin

стоит ещё упомянуть такой способ центрирования по горизонтали и по вертикали, но без translate():

``` css .parent { position: relative; }

.child { position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: auto; } ```

Автар пользователя
GarikFF

Заключение порадовало :-)

Автар пользователя
rosivanov

.parent { position: relative; } .child { position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: auto; } Это работает только если у .child заданы ширина и высота.