代码设置margin_margin: auto 的魔法世界

曹锋,医药支撑团队前端工程师。处女座,追求优雅的代码。

梦开始的地方

在 CSS 的世界里,各种居中问题可以说是时刻伴随着我们,其中 margin: auto 必须是当之无愧的童年记忆,甚至到了如今我们已经掌握了各种 CSS 的奇技淫巧,这段朴实无华的代码依旧占有一席之地,四个字来评价:yyds!让我们再来重温一下这段经典代码:

<style>.parent-panel {padding: 20px;background-color: darksalmon;
  }.main-panel {width: 60%;margin: auto;background-color: blanchedalmond;text-align-last: justify;
  }style>

<div class="parent-panel">
 <div class="main-panel">我居中了div>
div>

效果如下:

50a575e4db4736cee5e0aef1d371a086.png

margin: auto 解决了 让一个 正常布局流(normal flow)固定宽度 元素 水平 居中 的问题,其原理就是:在 writing-mode: horizontal-tbdirection: ltr 的前提下,当我们给一个块元素设置 margin-left: auto ( margin-right: auto ) 时,其计算值为该块元素的父级在水平方向上的可用剩余空间,二者都设置了就均分剩余空间,自然就让该元素水平方向居中了。

上面说了让固定宽度块元素水平居中的方法,自然会想到居左和居右,居左就不用说了,居右该如何实现呢?按照上面对 margin 值为 auto 的计算原理的解释,我们不难想到一个解决方案:通过设置该元素的 margin-left: auto 让其 margin-left 占据父元素左侧所有的可用剩余空间,将其挤到父元素最右侧从而实现居右效果,代码如下:

<style>.parent-panel {padding: 20px;background-color: darksalmon;
  }.main-panel {width: 60%;margin-left: auto;background-color: blanchedalmond;text-align-last: justify;
  }style>

<div class="parent-panel">
 <div class="main-panel">我现在居右了div>
div>

效果如下:

1324862351d6592180d3347491ea6af8.png

通过上面两个例子可以看到, margin 值为 auto 可以很方便的控制一个固定宽度的块元素在水平方向上的对齐方式,但其实能做的事情很有限,主要是因为我们对该元素的约束很多: 固定宽度块元素水平方向 ,而且因为本身是块元素,所以在正常文档流中该行只会存在一个块元素,那么我们如何控制一些更为复杂的场景下元素的对其方式呢,比如垂直方向、多元素的场景等,大家一定都已经想到了,那就是 Flexbox 布局方式了。

Flexbox 带来的更多可能

Flexbox 布局想必大家都已经很熟悉并且在实际开发中大量使用了,它的出现为开发者提供了更优雅的布局方案,甚至是以前无法单独使用 CSS 解决的问题,具体的一些常用场景可以查看 这里(https://github.com/philipwalton/solved-by-flexbox) ,我们这里就不展开讨论各种布局方案,主要还是看看 Flexbox 遇到 margin: auto 会有哪些有趣的故事。

Flexbox 布局本身就自带很多属性用来控制子元素在 主轴(main axis)交叉轴(cross axis) 上的对其方式: align-items align-self justify-content 等,想必大家都已经大量使用过了,但是对于子元素在 主轴 上对其方式的控制,没有一个 justify-self 用来让单个元素可以对自己进行特殊处理。而且在一些特殊场景下 justify-content 的效果会有问题,下面都会一一讲到。接下来让我们看看一些实际布局效果该如何实现。

左右对齐

看看下面这种非常常见的布局方式:

fdb277aa83a3e0b179a7c3408c9f0338.png

布局要求如下:所有子元素垂直方向居中对齐,水平方向上分成两个区域,左侧的「一键三连」主操作区,右侧的一些辅助操作区域,并且我们要求编写尽可能简洁的 html 代码,伪代码如下:

<ul class="operate-panel">
  
  <li class="item">点赞li>
  <li class="item">投币li>
  <li class="item">收藏li>
  <li class="item item-forward">转发li>
  
  
  <li class="item item-report">投诉li>
  <li class="item">笔记li>
  <li class="item">更多操作li>
ul>

我们首先想到的肯定是使用 Flexbox 进行布局,代码如下:

.operate-panel {
  display: flex;
  align-items: center;
}
.operate-panel .item + .item {
  margin-left: 2em;
}

上面的代码已经基本完成了布局要求,还差一个将辅助区域居右的该如何实现。一个思路是:让「转发」按钮 flex-grow: 1 将辅助区域挤到右边去,或者让「投诉」按钮 flex-grow: 1; text-align: right 也可以实现同样效果。代码如下:

.operate-panel .item.item-forward {
  flex-grow: 1;
}
/* or */
.operate-panel .item.item-report {
  flex-grow: 1;
  text-align: right;
}

但是上面代码有一个不好之处:元素宽度被拉伸了,这样可能会给元素内部的布局带来影响,比如要求元素最大宽度不能超过 100px ,超出部分显示省略号,所以我们的解决方法最好是不影响元素本身尺寸。

其实我们可以将左右两个区域分别用一个容器包裹起来,然后对着两个容器进行左右对齐布局,但是这样的话我们需要多写一些为了布局而存在的 html 标签,所以这个方案暂时不考虑。

那么接下来就看看 margin: auto 在这里能不能派上用场。我们先看看 margin: autoFlexbox 布局方式里的 计算方式是如何的:

Auto margins on flex items have an effect very similar to auto margins in block flow:

  • During calculations of flex bases and flexible lengths, auto margins are treated as 0.
  • Prior to alignment via justify-content and align-self, any positive free space is distributed to auto margins in that dimension.
  • Overflowing boxes ignore their auto margins and overflow in the end direction.

根据规范的定义,简单来说,在 flex items 上定义 margin: auto 的效果和上面讲到的在块元素上效果类似:占用父级剩余可用空间,但是有些不同的是, flex items 上的 margin: auto 不仅对水平方向有效,对垂直方向同样有效,OMG 用它用它用它!!! 直接看代码:

.operate-panel .item.item-forward{
 margin-right: auto;
}
/* or */
.operate-panel .item.item-report {
  margin-left: auto;
}

一个字:非常的优雅!这个问题就非常优雅的解决了,下一位。

带操作的页面顶部栏

标题有点绕,直接看效果图:

0dfdca7fd3cac25bdfa4056a8c877c9d.png

要求如下:左侧「返回」按钮居左,右侧操作区域居右,标题在 剩余可用空间 内水平 居中,所有子元素垂直方向居中。先看看 html 代码:

<ul class="header-panel">
  <li class="item">> 返回li>
  <li class="item item-title">我是标题li>
  <li class="item">清单li>
  <li class="item">搜索li>
  <li class="item">发布li>
ul>

不出意料我们还是使用 flexbox 进行布局,主要的 CSS 代码如下:

.header-panel {
  display: flex;
  align-items: center;
}
.header-panel .item + .item {
  margin-left: 2em;
}

现在主要解决的问题是「标题」的定位,可以思考一下,在 flexbox 提供的现有对齐方式里,我们找不到解决方案,除非我们改造 html 代码将左侧和右侧区域分别用一个容器包裹起来,然后在父元素上 justify-content: space-between 。可以,但不优雅。

再看一遍要求:标题在 剩余可用空间 内水平 居中margin: auto 直呼我擅长。上一个例子是要求居右所以我们让某一个关键元素的 margin-left: auto 或者 margin-right: auto ,这里要求居中,那我们就给左右都 auto 均分剩余空间:

.header-panel .item.item-title {
  margin: 0 auto;
}

其实这里还有一个设置方法:给「标题」左侧的「返回」加一个 margin-right: auto ,再给「标题」右侧的「清单」加一个 margin-left: auto ,效果一样。可以,但没必要。这个问题到此也就完美解决了,下一位。

margin: auto :我要篡位

经历了上面的代码,相信大家已经对 margin: auto 这段朴实无华的代码肃然起敬,请把「膨胀」打在公屏上。 margin: auto 现在很膨胀,塔门说:诶,你 justify-content 不好使,也不要给我说什么 align-items align-self ,老夫搞布局就是一把 margin: auto 复制!粘贴!哪里不齐贴哪里!那么事实真是如此吗?我们今天有幸请到了 margin: auto 马老师本码,来为我们讲解一下,以下是来自现场的文字报道。

<ul class="flex-panel">
  <li class="item">li>
  <li class="item">li>
  <li class="item">li>
ul>

.flex-panel {
  display: flex;
}

/* align-items: center */
.flex-panel > .item {
  margin: auto 0;
}

/* justify-content: center */
.flex-panel > .item:first-child {
  margin-left: auto;
}
.flex-panel > .item:last-child {
  margin-right: auto;
}

/* justify-content: space-around */
.flex-panel > .item {
  margin: 0 auto;
}

/* justify-content: space-between */
.flex-panel > .item + .item {
  margin-left: auto;
}

/* justify-content: space-evenly */
.flex-panel > .item {
  margin-left: auto;
}
.flex-panel > .item:last-child {
  margin-right: auto;
}

以上只列出了部分实现,其实所有 justify-contentalign-items 能实现的对齐效果使用 margin: auto 都可以实现,大家可以自己试一试其它效果,这里就不再赘述。但是您可能要问了:既然人家 flexbox 自带的都已经实现了这些对齐方式,这里还有必要再用 margin: auto 来实现嘛。这就是我最开始讲到的:

而且在一些特殊场景下 justify-content 的效果会有问题

来看一个场景:

9b07d35957361276289b9d7b5e3391b5.png
底部栏空间充足
3a66eb38c0d359511282b6bdaf8a04a6.png
底部栏空间不足滚动

要求如下:每一块的宽度固定 width: 30% 不可伸缩,父元素剩余可用空间均分到各块之间作为间隔,当数量过多宽度超出父级容器时滚动。我们可以写出如下代码:

<style>.bottom-panel {display: flex;justify-content: space-evenly;overflow: auto;
  }.bottom-panel > .item {width: 30%;flex-shrink: 0;
  }style>

<ul class="bottom-panel">
  <li class="item">li>
  <li class="item">li>
  <li class="item">li>
ul>

这段代码在子元素数量较少不会超出父级容器时表现非常完美,但是,如果子元素数量超过 3 个时,所有子元素宽度之和大于 100% 必然超出父元素,此时的效果就你太正常了,左侧的部分子元素整个或者部分会被隐藏,而且通过滚动父元素也无法令其显示,如下:

<style>.bottom-panel {display: flex;justify-content: space-evenly;overflow: auto;
  }.bottom-panel > .item {width: 30%;flex-shrink: 0;
  }style>

<ul class="bottom-panel">
  <li class="item">li>
  <li class="item">li>
  <li class="item">li>
  <li class="item">li>
  <li class="item">li>
  <li class="item">li>
ul>

7ab4c2eb51aa9ced8dea3632c48adce6.png
底部栏部分被隐藏

这个问题在 justify-content: center justify-content: space-around 的时候同样存在,规范里定义了一个 safe 来解决这个问题,但是很遗憾目前浏览器支持情况还非常差。

margin: auto 总是会在你最需要的时候忽然出现。

再看一下上面提到的规范定义:

Overflowing boxes ignore their auto margins and overflow in the end direction.

在这种情况下 margin: auto 会选择默默的消失,还你一份 love and peace

<style>.bottom-panel {display: flex;overflow: auto;
  }.bottom-panel > .item {width: 30%;flex-shrink: 0;margin-left: auto;
  }.bottom-panel > .item:last-child {margin-right: auto;
  }style>

<ul class="bottom-panel">
  <li class="item">li>
  <li class="item">li>
  <li class="item">li>
  <li class="item">li>
  <li class="item">li>
  <li class="item">li>
ul>

总结

相信大家现在对 margin: auto 的认识和使用都有了更加深刻的印象,尤其和 flexbox 双剑合璧会带来很多美妙的魔法般的体验,flexbox 本身就是非常强大的布局方案,有了 margin: auto 的加持更是如虎添翼。文中如果有错误或者表达不妥的地方,欢迎大家拍砖助我进步。我本人对 CSS 非常热爱,喜欢用优雅的方式实现页面布局,也非常欢迎大家一起来探讨。

margin: auto ,永远滴神!

参考资料

  • margin 系列之 keyword auto (https://www.ituring.com.cn/article/64580)
  • solved by flexbox (https://github.com/philipwalton/solved-by-flexbox)
  • Aligning with auto margins (https://www.w3.org/TR/css-flexbox-1/#auto-margins)
  • What's the difference between margin:auto and justify-content / align-items center? (https://stackoverflow.com/questions/44244549/whats-the-difference-between-marginauto-and-justify-content-align-items-cent)
  • In CSS Flexbox, why are there no “justify-items” and “justify-self” properties? (https://stackoverflow.com/questions/32551291/in-css-flexbox-why-are-there-no-justify-items-and-justify-self-properties/33856609#33856609)
  • Can't scroll to top of flex item that is overflowing container (https://stackoverflow.com/questions/33454533/cant-scroll-to-top-of-flex-item-that-is-overflowing-container)
  • Overflow Alignment: the safe and unsafe keywords and scroll safety limits (https://www.w3.org/TR/css-align-3/#overflow-values)

ad6024f99c88cc3b9f8d6b619de510d2.png

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
用sql解析一下以下josn: [{"supply_id":"c05debb4-55d5-4c46-85e9-b0ae4b40658d","is_enabled":true,"supply_code":3251,"supply_name":"乐颐食品(深圳)有限公司","before_cost_unit":390,"after_cost_unit":360,"diff_cost_unit":-30,"diff_cost_unit_percentage":-7.69,"before_gross_margin_percentage":21.69,"after_gross_margin_percentage":27.71,"diff_gross_margin_percentage":6.02},{"supply_id":"c7cb2c72-d608-46dc-9650-b31d922fc515","is_enabled":false,"supply_code":3690,"supply_name":"云南绿之恋农业科技有限公司","before_cost_unit":390,"after_cost_unit":360,"diff_cost_unit":-30,"diff_cost_unit_percentage":-7.69,"before_gross_margin_percentage":21.69,"after_gross_margin_percentage":27.71,"diff_gross_margin_percentage":6.02},{"supply_id":"7bcc9770-e28b-45af-96e1-dbd15c337658","is_enabled":true,"supply_code":4410,"supply_name":"广州绿之恋农业科技有限公司","before_cost_unit":390,"after_cost_unit":360,"diff_cost_unit":-30,"diff_cost_unit_percentage":-7.69,"before_gross_margin_percentage":21.69,"after_gross_margin_percentage":27.71,"diff_gross_margin_percentage":6.02},{"supply_id":"8625f2f8-f661-490a-9e55-4aa1cae5a8a6","is_enabled":true,"supply_code":1504,"supply_name":"深圳市正禾蔬菜有限公司","before_cost_unit":390,"after_cost_unit":360,"diff_cost_unit":-30,"diff_cost_unit_percentage":-7.69,"before_gross_margin_percentage":21.69,"after_gross_margin_percentage":27.71,"diff_gross_margin_percentage":6.02},{"supply_id":"49481833-e099-4eaf-9c9b-ddf4c63fbe63","is_enabled":true,"supply_code":1396,"supply_name":"福州优野生态农业有限公司(佳素)","before_cost_unit":390,"after_cost_unit":360,"diff_cost_unit":-30,"diff_cost_unit_percentage":-7.69,"before_gross_margin_percentage":21.69,"after_gross_margin_percentage":27.71,"diff_gross_margin_percentage":6.02},{"supply_id":"0d55599b-c6a6-4896-99e5-09f9379b5c8b","is_enabled":false,"supply_code":1205,"supply_name":"福州耕韵生态农业开发有限公司","before_cost_unit":390,"after_cost_unit":360,"diff_cost_unit":-30,"diff_cost_unit_percentage":-7.69,"before_gross_margin_percentage":21.69,"after_gross_margin_percentage":27.71,"diff_gross_margin_percentage":6.02},{"supply_id":"fa417a5a-d275-44bf-895b-d03aae5b932e","is_enabled":true,"supply_code":4171,"supply_name":"遂宁市旭塘农业有限公司","before_cost_unit":390,"after_cost_unit":360,"diff_cost_unit":-30,"diff_cost_unit_percentage":-7.69,"before_gross_margin_percentage":21.69,"after_gross_margin_percentage":27.71,"diff_gross_margin_percentage":6.02}]
07-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值