探秘 flex 上下文中神奇的自動 margin

2021-03-02 前端雜貨鋪

為了引出本文的主題,先看看這個問題,最快水平垂直居中一個元素的方法是什麼?

水平垂直居中也算是 CSS 領域最為常見的一個問題了,不同場景下的方法也各不相同,各有優劣。嗯,下面這種應該算是最便捷的了:

<div class="g-container">
<div class="g-box"></div>
</div>

.g-container {
display: flex;
}

.g-box {
margin: auto;
}

上面的 display: flex 替換成 display: inline-flex | grid | inline-grid 也是可以的。

如何讓 margin: auto 在垂直方向上居中元素

嗯。這裡其實就涉及了一個問題,如何讓 margin: auto 在垂直方向上生效?

換句話說,傳統的 display: block BFC(塊格式化上下文)下,為什麼 margin: auto 在水平方向可以居中元素在垂直方向卻不行?

通常我們會使用這段代碼:

div {
width: 200px;
height: 200px;
margin: 0 auto;
}

讓元素相對父元素水平居中。但是如果我們想讓元素相對父元素水平垂直居中的話,使用 margin: auto 0是不生效的。

BFC 下 margin: auto 垂直方向無法居中元素的原因

查看 CSS 文檔,原因如下,在 BFC 下:

If both margin-left and margin-right are auto, their used values are equal, causing horizontal centring.

—CSS2 Visual formatting model details: 10.3.3

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

—CSS2 Visual formatting model details: 10.6.3

簡單翻譯下,在塊格式化上下文中,如果 margin-left 和 margin-right 都是 auto,則它們的表達值相等,從而導致元素的水平居中。( 這裡的計算值為元素剩餘可用剩餘空間的一半)

而如果 margin-top 和 margin-bottom 都是 auto,則他們的值都為 0,當然也就無法造成垂直方向上的居中。

使用 FFC/GFC 使 margin: auto 在垂直方向上居中元素

OK,這裡要使單個元素使用 margin: auto 在垂直方向上能夠居中元素,需要讓該元素處於 FFC(flex formatting context),或者 GFC(grid formatting context) 上下文中,也就是這些取值中:

{
display: flex;
display: inline-flex;
display: grid;
display: inline-grid;
}

FFC 下 margin: auto 垂直方向可以居中元素的原因

本文暫且不談 grid 布局,我們業務中需求中更多的可能是使用 flex 布局,下文將著重圍繞 flex 上下文中自動 margin 的一些表現。

嗯,也有很多前端被戲稱為 flex 工程師,什麼布局都 flex 一把梭。

查看 CSS 文檔,原因如下,在 dispaly: flex 下:

CSS Flexible Box Layout Module Level 1 -- 8.1. Aligning with auto margins

簡單翻譯一下,大意是在 flex 格式化上下文中,設置了 margin: auto 的元素,在通過 justify-content和 align-self 進行對齊之前,任何正處於空閒的空間都會分配到該方向的自動 margin 中去

這裡,很重要的一點是,margin auto 的生效不僅是水平方向,垂直方向也會自動去分配這個剩餘空間。

使用自動 margin 實現 flex 布局下的 space-between | space-around

了解了上面最核心的這一句 :

在通過 justify-content 和 align-self 進行對齊之前,任何正處於空閒的空間都會分配到該維度中的自動 margin 中去

之後,我們就可以在 flex 布局下使用自動 margin 模擬實現 flex 布局下的 space-between 以及 space-around 了。

自動 margin 實現 space-around

對於這樣一個 flex 布局:

<ul class="g-flex">
<li>liA</li>
<li>liB</li>
<li>liC</li>
<li>liD</li>
<li>liE</li>
</ul>

如果它的 CSS 代碼是:

.g-flex {
display: flex;
justify-content: space-around;
}

li { ... }

效果如下:

那麼下面的 CSS 代碼與上面的效果是完全等同的:

.g-flex {
display: flex;
// justify-content: space-around;
}

li {
margin: auto;
}

自動 margin 實現 space-between

同理,使用自動 margin,也很容易實現 flex 下的 space-between,下面兩份 CSS 代碼的效果的一樣的:

.g-flex {
display: flex;
justify-content: space-between;
}

li {...}

.g-flex {
display: flex;
// justify-content: space-between;
}

li {
margin: auto;
}

li:first-child {
margin-left: 0;
}

li:last-child {
margin-right: 0;
}

當然,值得注意的是,很重要的一點:

Note: If free space is distributed to auto margins, the alignment properties will have no effect in that dimension because the margins will have stolen all the free space left over after flexing.

CSS Flexible Box Layout Module Level 1 -- 8.1. Aligning with auto margins

意思是,如果任意方向上的可用空間分配給了該方向的自動 margin ,則對齊屬性(justify-content/align-self)在該維度中不起作用,因為 margin 將在排布後竊取該緯度方向剩餘的所有可用空間。

也就是使用了自動 margin 的 flex 子項目,它們父元素設置的 justify-content 已經它們本身的 align-self 將不再生效,也就是這裡存在一個優先級的關係。

使用自動 margin 實現 flex 下的 align-self: flex-start | flex-end | center

自動 margin 能實現水平方向的控制,也能實現垂直方向的控制,原理是一樣的。

用 margin: auto 模擬 flex 下的 align-self: flex-start | flex-end | center,可以看看下面幾個 Demo:

不同方向上的自動 margin

OK,看完上面的一大段鋪墊之後,大概已經初步了解了 FFC 下,自動 margin 的神奇。

無論是多個方向的自動 margin,抑或是單方向的自動 margin,都是非常有用的。

再來看幾個有意思的例子:

使用 margin-left: auto 實現不規則兩端對齊布局

假設我們需要有如下布局:

DOM 結構如下:

<ul class="g-nav">
<li>導航A</li>
<li>導航B</li>
<li>導航C</li>
<li>導航D</li>
<li class="g-login">登陸</li>
</ul>

對最後一個元素使用 margin-left: auto,可以很容易實現這個布局:

.g-nav {
display: flex;
}

.g-login {
margin-left: auto;
}

此時, auto 的計算值就是水平方向上容器排列所有 li 之後的剩餘空間。

當然,不一定是要運用在第一個或者最後一個元素之上,例如這樣的布局,也是完全一樣的實現:

<ul class="g-nav">
<li>導航A</li>
<li>導航B</li>
<li>導航C</li>
<li>導航D</li>
<li class="g-login">登陸</li>
<li>註冊</li>
</ul>

.g-nav {
display: flex;
}

.g-login {
margin-left: auto;
}

垂直方向上的多行居中

OK,又或者,我們經常會有這樣的需求,一大段複雜的布局中的某一塊,高度或者寬度不固定,需要相對於它所在的剩餘空間居中:

這裡有 5 行文案,我們需要其中的第三、第四行相對於剩餘空間進行垂直居中。

這裡如果使用 flex 布局,簡單的 align-self 或者 align-items 好像都沒法快速解決問題。

而使用自動 margin,我們只需要在需要垂直居中的第一個元素上進行 margin-top: auto,最後一個元素上進行 margin-bottom: auto 即可,看看代碼示意:

<div class="g-container">
<p>這是第一行文案</p>
<p>這是第二行文案</p>
<p class="s-thirf">1、剩餘多行文案需要垂直居中剩餘空間</p>
<p class="s-forth">2、剩餘多行文案需要垂直居中剩餘空間</p>
<p>這是最後一行文案</p>
</div>

.g-container {
display: flex;
flex-wrap: wrap;
flex-direction: column;
}

.s-thirf {
margin-top: auto;
}

.s-forth {
margin-bottom: auto;
}

當然,這裡將任意需要垂直居中剩餘空間的元素用一個 div 包裹起來,對該 div 進行 margin: auto 0也是可以的。

嗯,非常的好用且方便:CodePen Demo -- 自動margin快速垂直居中任意段落

使用 margin-top: auto 實現粘性 footer 布局

OK,最後再來看這樣一個例子。

要求:頁面存在一個 footer 頁腳部分,如果整個頁面的內容高度小於視窗的高度,則 footer 固定在視窗底部,如果整個頁面的內容高度大於視窗的高度,則 footer 正常流排布(也就是需要滾動到底部才能看到 footer),算是粘性布局的一種。

看看效果:

嗯,這個需求如果能夠使用 flex 的話,使用 justify-content: space-between 可以很好的解決,同理使用 margin-top: auto 也非常容易完成:

<div class="g-container">
<div class="g-real-box">
...
</div>
<div class="g-footer"></div>
</div>

.g-container {
height: 100vh;
display: flex;
flex-direction: column;
}

.g-footer {
margin-top: auto;
flex-shrink: 0;
height: 30px;
background: deeppink;
}

上面的例子旨在介紹更多自動 margin 的使用場景。當然,這裡不使用 flex 布局也是可以實現的,下面再給出一種不藉助 flex 布局的實現方式:

CodePen Demo -- sticky footer by margin/paddig

值得注意的點

自動 margin 還是很實用的,可以使用的場景也很多,有一些上面提到的點還需要再強調下:

塊格式化上下文中margin-top 和 margin-bottom 的值如果是 auto,則他們的值都為 0

flex 格式化上下文中,在通過 justify-content 和 align-self 進行對齊之前,任何正處於空閒的空間都會分配到該方向的自動 margin 中去

單個方向上的自動 margin 也非常有用,它的計算值為該方向上的剩餘空間

使用了自動 margin 的 flex 子項目,它們父元素設置的 justify-content 以及它們本身的 align-self將不再生效

最後

更多精彩 CSS 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

所有 Demo 點擊原文連結都有對應 CodePen 連結。

好了,本文到此結束,希望對你有幫助 :)

如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

歡迎關注「前端雜貨鋪」,一個有溫度的雜貨鋪,專注於前端乾貨分享

相關焦點

  • 關於css margin,你需要知道的一切
    「盒模型」中的元素之一是margin,即盒子周圍的透明區域,它會將其他元素從盒子內容中推開。CSS1中描述了 margin-top、margin-right、margin-bottom和margin-left屬性,以及一次設置所有四個屬性的簡寫 margin。margin看起來是一個相當簡單的事情,但是,在本文中,咱們將看一些在使用margin一些讓人迷惑有有趣的事情。
  • flex布局實現瀑布流專題及常見問題 - CSDN
    瀑布流的布局自我感覺還是很吸引人的,最近又看到實現瀑布流這個做法,在這裡記錄下,特別的,感覺flex布局實現瀑布流還是有點懵的樣子,不過現在就可以明白它的原理了1.multi-column多列布局實現瀑布流先簡單的講下multi-column相關的部分屬性column-count
  • 【有趣的 CSS 題目三】 層疊順序與堆棧上下文知多少
    解題不考慮兼容性,題目天馬行空,想到什麼說什麼,如果解題中有你感覺到生僻的 CSS 屬性,趕緊去補習一下吧。不斷更新,不斷更新,不斷更新,重要的事情說三遍。會看到,inline-block 的 div 不再一定疊在 float 的 div 之上,而是和 HTML 代碼中 DOM 的堆放順序有關,後添加的 div 會 疊在先添加的 div 之上。這裡的關鍵點在於,添加的 opacity:0.9 這個讓兩個 div 都生成了 stacking context(堆疊上下文) 的概念。
  • CSS 中你需要知道 auto 的一切!
    在CSS中,我們有auto值,它可以用於像margin,position,height,width等屬性。在本文中,會先解釋auto的工作方式以及如何最大程度地利用auto的技術細節,當然,會配合一些用例和示例。簡介 auto關鍵字的使用因屬性而異。對於本文,我將在每個屬性的上下文中解釋值。
  • 有趣的 CSS 題目(3): 層疊順序與堆棧上下文知多少
    重點來了,請注意,上面的比較是基於兩個 div 都沒有形成 堆疊上下文 這個為基礎的。會看到,inline-block 的 div 不再一定疊在 float 的 div 之上,而是和 HTML 代碼中 DOM 的堆放順序有關,後添加的 div 會 疊在先添加的 div 之上。這裡的關鍵點在於,添加的 opacity:0.9 這個讓兩個 div 都生成了 stacking context(堆疊上下文) 的概念。
  • 前端教程:flex布局(攜程網首頁案例製作)
    display:flex 這句話是為父元素設置的 父元素中的子元素變成伸縮項 。當我們為父盒子設為 flex 布局以後,子元素的 float、clear 和 vertical-align 屬性將失效。
  • flex 多列水平居中 - CSDN
    CSS布局布局是CSS中一個重要部分,本文總結了CSS布局中的常用技巧,包括常用的水平居中、垂直居中方法,以及單列布局、多列布局的多種實現方式(包括傳統的盒模型布局和比較新的flex布局實現),希望能給需要的小夥伴帶來一些幫助。
  • Vue全家桶之flex布局探秘
    ,不過我們使用webpack打包,所以就不存在這樣的問題,它會為我們自動添加瀏覽器前綴;還有一點,設置了flex布局之後,其子元素的float、clear、vertical-align屬性都將不起作用。
  • 深入理解CSS中的層疊上下文和層疊順序
    我們大家可能都熟悉CSS中的z-index屬性,需要跟大家講的是,z-index實際上只是CSS層疊上下文和層疊順序中的一葉小舟。一、什麼是層疊上下文層疊上下文,英文稱作」stacking context」. 是HTML中的一個三維的概念。如果一個元素含有層疊上下文,我們可以理解為這個元素在z軸上就「高人一等」。
  • 49 張 GIF 圖中學習 49 個 CSS 知識點
    04.【BFC應用】💔BFC應用之消除浮動的影響05.【flex不為認知的特性之一】💕flex布局下margin:auto的神奇用法06.【flex不為認知的特性之二】💖flex布局,當flex-grow之和小於1時,只能按比例分配部分剩餘空間,而不是全部
  • CSS中固定定位、相對定位、絕對定位以及flex布局高效定位
    5.設置left和right 負的外邊距  我們的目標是讓left、main、right依次並排,但是上圖中left和right都是位於下一行,這裡的技巧就是使用負的margin-left   負的margin-left會讓元素沿文檔流向左移動,如果負的數值比較大就會一直移動到上一行。關於負的margin的應用也是博大精深,這裡肯定是不能詳細介紹了。
  • CSS中重要的BFC
    而普通流其實就是指BFC中的FC。FC(Formatting Context),直譯過來是格式化上下文,它是頁面中的一塊渲染區域,有一套渲染規則,決定了其子元素如何布局,以及和其他元素之間的關係和作用。常見的FC有BFC、IFC,還有GFC和FFC。
  • 微信小程序自定義日曆組件及flex布局最後一行對齊問題分析
    正文在編寫過程中,因為大家都知道,日曆組件是有固定行數和每一行的固定列數的(即使當前方塊內沒有值),所以結合小程序「數據優先」的特點,最合適的布局方式一定是flex了!在本項目中,我的解決方法很簡單:將這一行代碼去掉,那麼由此導致的寬高問題怎麼解決?這個問題,css給出了解決方案—— calc() !
  • 深入了解 Flex 屬性
    你有沒有想過 CSS 中的 flex屬性如何工作?它是 flex-grow,flex-shrink和flex-basis的簡寫。開發中最常見的寫法是flex:1,它表示 flex 項目擴展並填充可用空間。接下來,我們來詳細看看它表示是什麼意思。
  • 2020年了,FLEX布局還搞不定麼
    閱讀本文您將收穫布局的相關概念Flex布局的相關屬性面試中常問的Flex相關知識為啥要Flex布局?因為貪婪!它的默認值為auto,即項目的本來大小flex: flex-grow, flex-shrink 和 flex-basis的簡寫默認值為flex: 0 1 auto;後兩個屬性可選align-self: 自定義的主軸對齊方式允許單個項目有與其他項目不一樣的對齊方式,可覆蓋 align-items 屬性。
  • 在H5開發中flex布局方式的屬性詳解
    我們知道flex布局就是通過給父盒子添加flex屬性,來控制子盒子的位置和排列方式 ,那麼關於flex屬性有哪些 ,又怎麼去用呢?一父項常見屬性(父盒子)flex-direction:設置主軸的方向在 flex 布局中,是分為主軸和側軸兩個方向,同樣的叫法有 : 行和列、x 軸和y 軸注意: 主軸和側軸是會變化的
  • ...flex;  animation: all 1s linear;}自動添加css3前綴後div...
    使用webstorm自動編譯scss文件,並對生成的css文件自動添加瀏覽器支持前綴css書寫div {  display: flex;  animation: all 1s linear;}自動添加css3前綴後div {  display: -webkit-box;  display: -