Flex 居中应该这么写

假设现有个很简单的需求,让 item 在 container 容器内居中:

html 复制代码
<div class="container">
  <div class="item">居中</div>
</div>

当前 CSS 样式为:

css 复制代码
.container {
  width: 500px;
  height: 300px;
  background-color: yellow;
}
.item {
  padding: 10px;
  background-color: lightgreen;
}

当前的效果如下:

而想要的效果区域在黄色容器内上下和左右都居中。很简单,只要给 container 容器设置为 flex 布局,并且在主轴和交叉轴上都居中即可,代码如下:

css 复制代码
.container {
  display: flex;
  align-items: center;
  justify-content: center;
}

很快就能实现下面的效果了:

然而,这样居中是有问题的,请不要这么写代码,笔者推荐的方式是:

css 复制代码
.container {
  display: flex;
}

.item {
  margin: auto;
}

即不要给容器设置 justify-contentalign-items 属性,而是给子元素设置 margin: auto,可以实现同样的效果:

原因分析

为什么要这么设置呢?问题出现在当 Item 的内容不再是简单的「居中」两个字,而是很长一段文本,导致撑满了整个 flex 布局的容器的时候,请看:

html 复制代码
<div class="container">
  <div class="item">当乌鸦飞过山头,云彩在空中飘荡,太阳逐渐升起,洒下金色的光芒,照亮了整个大地,万物苏醒,迎接新的一天的到来。在这美丽的早晨里,鲜花绽放,鸟儿欢唱,微风轻拂着脸庞,带来一丝凉意,让人感到宁静而舒适。
    小时候,总幻想着长大后的自己会过上理想中的生活,拥有自己的家庭、事业和幸福。而如今,当我回望过去,发现生活没有想象中的那么简单,充满了各种考验和挑战。但我并不畏惧,因为我相信坚持和努力能够克服一切困难,让梦想成为现实。
    在这个充满机会和竞争的世界里,每个人都在追寻着自己的梦想。有人选择脚踏实地,默默付出,一步一个脚印地追求成功;有人选择勇往直前,大胆冒险,不惧失败地追求突破。而我,我希望能够找到自己的定位,做自己喜欢的事情,用心去追求,用行动去证明自己的价值。
    人生就像一本书,每个人都是其中的主角,我们要用自己的双手去书写精彩的篇章。无论遇到什么困难和挫折,我相信只要坚持不懈,勇往直前,就一定能够战胜困难,实现自己的梦想。
    所以,让我们一起迎接生活的挑战,抓住机遇,勇往直前,相信自己的能力,努力奋斗,让每一天都过得有意义,活出自己想要的精彩人生!
  </div>
</div>

此时,文本太长,超出容器范围了:

可以看到,开头的文本被截断了,并没有显示出来!这就是因为给 flex 设置 align-items: center 导致的。即使设置容器的 overflow 属性,让里面的文本能够滚动。顶部溢出的文本依然显示不出来。

而此时如果你使用的是 margin: auto 的居中方案,则不会出现此问题,开头的文本能够正常显示:

类似案例

上面是问题是因为 flex 容器内的元素在交叉轴方向上溢出了,顶部区域不可见。类似的,当左右布局,在主轴方向超出溢出的时候,也会导致开始左侧区域内容不可见,例如:

下面的代码可以复现上面的案例,其中 DOM 结构如下:

html 复制代码
<div class="flex-container">
  <div class="item" style="background-color: yellow;">
    <h2>box #1</h2>
    <p>this box disappears and can't be accessed via scroll</p>
  </div>
  <div class="item" style="background-color: antiquewhite;">
    <h2>box #2</h2>
    <p>this box is fully accessible at all times
    </p>
  </div>
  <div class="item" style="background-color: lightgreen;">
    <h2>box #2</h2>
    <p>this box is fully accessible at all times
  </div>
</div>

CSS 代码如下:

css 复制代码
.flex-container {
  display: flex;
  justify-content: center;
}

.item {
  flex: 1;
}

.item p {
  white-space: nowrap;
}

这里的关键是因为 justify-content: center 导致的,去掉即可。

底层设计

造成上述问题的原因是 flex 布局本身的缺陷导致的:

Flexbox's alignment properties do "true" centering, unlike other centering methods in CSS. This means that the flex items will stay centered, even if they overflow the flex container.
This can sometimes be problematic, however, if they overflow past the top edge of the page, or the left edge [...], as you can't scroll to that area, even if there is content there!

为了彻底解决这个问题,CSS 标准增加了 safeunsafe 两个关键字:

用户可以在设置 flex 元素居中的时候指定是 safe 模式:

css 复制代码
justify-content: safe center;
align-self: safe center;

可是这个新属性的兼容性并不是很好,大家在项目中谨慎使用:

相关推荐
y先森26 分钟前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy26 分钟前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu108301891129 分钟前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿2 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡3 小时前
commitlint校验git提交信息
前端
虾球xz3 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇3 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒3 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员3 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐4 小时前
前端图像处理(一)
前端