为什么 "margin: auto" 不将元素垂直居中?

Why doesn't "margin: auto" center an element vertically?

正如您在下面的演示中看到的,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>

我的问题不是寻求解决方法。

为什么...因为 W3C spec 是这么说的。

If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0.

至于实际的 "why"...查询确实应该在那里解决。

如前所述,此行为在 section 10.6.2 of CSS2.1, and has remained unchanged from CSS2 中指定。

积木箱从上到下垂直堆叠,正常流动。此外,例如 vertical margins may collapse, and only do so under certain circumstances (in your demo, the border on the parent element will prevent any margins on the child element from collapsing with its own). If you only have one such block box, and the height of the containing block is auto, then its top and bottom margins will be zero anyway. But if you have more than one block box in the same flow, or even out-of-flow boxes affecting the layout of in-flow boxes (in the case of clearance),您希望如何解决那些 in-flow 框的自动边距?

这就是内联元素(包括原子内联)和浮动元素(尽管水平边距永远不会折叠)的自动左右边距同样被清零的原因。 Inline-level 个盒子是 laid along line boxes, and floats too obey unique layout rules.

绝对定位的盒子是另一回事:因为它们永远不会意识到与自己处于相同定位上下文中的任何其他盒子,auto top and bottom margins can be calculated for them with respect to their containing blocks无需担心任何其他盒子的干扰。

Flexbox 也是一个不同的故事:flex 布局与块布局的不同之处在于,根据定义,flex 项总是知道同一 flex 格式化上下文中的其他 flex 项,包括 none.特别是 neither can floats intrude into the flex container, nor can you float flex items to subvert this (although you can still remove a child element from flex layout completely with absolute positioning). Margins behave very differently with flex items due in part to this. See sections 4.2, 9.5 and 9.6.

它不会使元素垂直居中,因为它是正常流中的 block-level 元素。因此,following rule applies:

If margin-top, or margin-bottom are auto, their used value is 0.

还值得指出的是,上述规则也适用于以下元素:(有关更多信息和条件,请参阅要点 10.6.2 and 10.6.3)。

  • 行内替换元素
  • Block-level 以正常流程替换元素
  • inline-block 以正常流程替换元素
  • 浮动替换元素
  • overflow 计算为 visible 时,
  • Block-level non-replaced 元素在正常流中

话虽这么说,绝对定位,non-replaced 没有 topheightbottom 值的 auto 元素是这条规则的例外。以下适用于 point 10.6.4

If none of the three top, height, and bottom are auto and if both margin-top and margin-bottom are auto, solve the equation under the extra constraint that the two margins get equal values.

请参阅下面的示例,演示如何使用 margin: auto 将绝对定位的元素垂直居中。它起作用是因为 topheightbottom 三个属性中的 none 的值为 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>

此外,可能还值得指出 following rule

If one of margin-top or margin-bottom is auto, solve the equation for that value. If the values are over-constrained, ignore the value for bottom and solve for that value.

这意味着如果绝对定位元素的margin-top值为automargin-bottom值为0(即margin: auto auto 0) ,该元素将绝对定位在相对于父元素的底部,如下例所示:

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

这是因为实际可能知道要在其中垂直居中的元素的真实高度。要理解这一点,首先要考虑自动水平居中的工作原理。您有一个 div 并为其指定了宽度(固定或百分比)。宽度可以计算到一定程度。如果它是固定宽度,那就太好了。如果它是灵活的或响应性的(百分比),至少你有一个宽度在到达下一个断点之前将覆盖的范围。你取那个宽度,减去它里面的任何东西,然后把剩下的分成两边。

现在,有了这些信息,浏览器如何计算出您的 div 垂直增长的无限变化量?请记住元素的大小、文本的换行、填充和响应性也会改变宽度并强制文本进一步换行,等等。

这是不可能完成的任务吗?不是真的,CSS 有没有花时间和精力来报道这个?我猜不值得他们花时间。

这基本上就是我告诉学生的答案。

但是....别担心! Bootstrap v4 alpha 已经搞清楚 vertical centering

编辑

抱歉编辑晚了,但我想你可能想考虑这个垂直居中的解决方案,使用 calc 函数非常简单

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

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

看到了HERE

Why doesn't margin:auto work vertically?

实际上,它确实如此——只是不是每个 display 值。

如果 displayflexmargin: auto 垂直和水平居中。

同样适用于display: inline-flexdisplay: griddisplay: 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>