Почему "margin: auto" не центрирует элемент по вертикали?

Как вы можете видеть в демонстрации ниже, margin: auto; центрирует синий div по горизонтали, но не по вертикали. Почему бы и нет?

.box {
  border: 1px solid red;
  width: 100px;
  height: 100px;
}
.center {
  background: blue;
  width: 50px;
  height: 50px;
  margin: auto;
}
<div class="box">
  <div class="center"></div>
</div>

Ответ 1

Как уже упоминалось, это поведение указано в разделе 10.6.2 в CSS2.1 и остается неизменным от CSS2.

Блок-блоки штабелируются вертикально сверху вниз в нормальном потоке. Кроме того, вертикальные поля могут свернуть и делать это только при определенных обстоятельствах (в вашей демонстрации граница на родительском элементе будет предотвращать любые поля на дочерний элемент от сворачивания с его собственным). Если у вас есть только один такой блок, а высота содержащего блока будет автоматически, то его верхнее и нижнее поля будут равно нулю. Но если у вас более одного блока в одном и том же потоке или даже вне потоковых ящиков, влияющих на расположение полей в потоке (в случае клиренс), как бы вы ожидали, что автоматические поля будут разрешены для тех, кто находится в потоке?

Вот почему автоматическое левое и правое поля также обнуляются для встроенных элементов (включая атомные строки) и плавает (хотя горизонтальные поля никогда не рушится). В строковых шкафах укладывается вдоль линейных блоков и плавает тоже повиноваться уникальные правила компоновки.

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

Flexbox также представляет собой разную историю: то, что устанавливает гибкую раскладку, отличную от макета блока, состоит в том, что элементы гибкости по определению всегда знают о других элементах гибкости в одном и том же контексте форматирования гибкости, в том числе о том, что их нет. В частности, ни один из них не может перемещаться в контейнер flex, и вы не можете плавать гибкие элементы, чтобы подорвать это (хотя вы все равно можете удалить дочерний элемент от гибкой схемы полностью с абсолютное позиционирование). Маржа ведет себя по-разному с использованием гибких элементов из-за этого. См. Разделы 4.2, 9.5 и 9.6.

Ответ 2

Почему... потому что спецификация W3C говорит об этом.

Если "margin-top" или "margin-bottom" являются "auto", их используемое значение равно 0.

Что касается фактического "почему"... вопрос действительно должен быть рассмотрен там.

Ответ 3

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

Если margin-top или margin-bottom являются auto, их используемое значение равно 0.

Также стоит отметить, что вышеприведенное правило также относится и к следующим элементам: (см. пункты 10.6.2 и 10.6.3 для получения дополнительной информации и условий).

  • Встроенные элементы
  • Элементы с замещением на уровне блоков в нормальном потоке
  • inline-block заменены элементы в нормальном потоке
  • Плавающие заменяемые элементы
  • Незаменяемые элементы на уровне блоков в нормальном потоке, когда overflow вычисляет visible

С учетом сказанного абсолютно исключенные элементы, которые не имеют значений top, height и bottom auto, являются исключением из этого правила. Следующее относится к пункту 10.6.4:

Если ни один из трех top, height и bottom не равен auto, и если оба margin-top и margin-bottom являются auto, решают уравнение под дополнительным ограничением, два поля получают равные значения.

См. пример ниже, демонстрирующий, как абсолютно позиционированный элемент вертикально центрируется с помощью margin: auto. Он работает, потому что ни одно из трех свойств top, height и bottom не имеет значения auto:

.box {
  border: 1px solid red;
  width: 100px;
  height: 100px;
  position: relative;
}
.center {
  background: blue;
  width: 50px;
  height: 50px;
  margin: auto;
  position: absolute;
  top: 0; right: 0;
  bottom: 0; left: 0;
}
<div class="box">
  <div class="center"></div>
</div>

Ответ 4

Почему не margin:auto работа по вертикали?

Фактически, это происходит - просто не для каждого display значения.

Если display flex, margin: auto центрируется как по вертикали, так и по горизонтали.

То же самое относится к display: inline-flex, display: grid и display: inline-grid.

.box {
  border: 1px solid red;
  width: 100px;
  height: 100px;
  display: flex; /* new */
}
.center {
  background: blue;
  width: 50px;
  height: 50px;
  margin: auto;
}
<div class="box">
  <div class="center"></div>
</div>

Ответ 5

Это из-за реальной возможности узнать истинную высоту элемента, в котором вы хотите центрировать вертикально. Чтобы понять это, сначала подумайте о том, как работает автоматическое горизонтальное центрирование. У вас есть div, который вы дали ему ширину (фиксированную или процентную). Ширина может быть рассчитана в определенной степени. Если фиксированная ширина, отлично. Если он гибкий или отзывчивый (в процентах), по крайней мере, у вас есть диапазон, который будет охватывать ширина до того, как он достигнет следующей точки останова. Вы берете эту ширину, минус все, что внутри, и разделяете остаток с обеих сторон.

Теперь, с этой информацией, как браузер мог вычислить бесконечное количество вариаций, в которых ваш div будет расти вертикально? Имейте в виду, что размер элемента, обтекание текста, paddings и отзывчивость также изменят ширину и заставят текст обернуться дальше, и дальше, и на нем.

Это невыполнимая задача? На самом деле, CSS потратил время и силы на это? Наверное, не стоит их времени.

И это в основном ответ, который я рассказываю своим ученикам.

Но... не волнуйся! Bootstrap v4 alpha вычислил вертикальное центрирование!

ИЗМЕНИТЬ

Извините, что отредактировал это поздно, но я подумал, что вы можете захотеть рассмотреть это решение, чтобы центрировать по вертикали, и это довольно просто, используя функцию calc

<div class="foo"></div>

.foo {
  background-color: red;
  height: 6em;
  left: calc(50% - 3em);
  position: absolute;
  top: calc(50% - 3em);
  width: 6em;
}

Смотрите ЗДЕСЬ