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

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

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

<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 也是能夠的。前端

CodePen Demo -- 使用 margin auto 水平垂直居中元素git

 

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

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

換句話說,傳統的 display: block BFC(塊格式化上下文)下,爲何 margin: auto 在水平方向能夠居中元素在垂直方向卻不行?佈局

一般咱們會使用這段代碼:flex

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

讓元素相對父元素水平居中。可是若是咱們想讓元素相對父元素垂直居中的話,使用 margin: auto 0是不生效的。flexbox

 

BFC 下 margin: auto 垂直方向沒法居中元素的緣由

查看 CSS 文檔,緣由以下,在 BFC 下:spa

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 下:

  • Prior to alignment via justify-content and align-self, any positive free space is distributed to auto margins in that dimension.

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 { ... }

效果以下:

image

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

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

li { 
    margin: auto;
}

CodePen Demo -- margin auto 實現 flex 下的 space-around

 

自動 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;
}

CodePen Demo -- margin auto 實現 flex 下的 space-between

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

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 實現不規則兩端對齊佈局

假設咱們須要有以下佈局:

image

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 以後的剩餘空間。

固然,不必定是要運用在第一個或者最後一個元素之上,例如這樣的佈局,也是徹底同樣的實現:

image

<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;
}

marginauto

Codepen Demo -- nav list by margin left auto

  

垂直方向上的多行居中

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

image

這裏有 5 行文案,咱們須要其中的第3、第四行相對於剩餘空間進行垂直居中。

這裏若是使用 flex 佈局,簡單的 align-self 或者 align-items 好像都無法快速解決問題。

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

<div class="g-container">
    <p>這是第一行文案</p>
    <p>這是第二行文案</p>
    <p class="s-thirf">一、剩餘多行文案須要垂直居中剩餘空間</p>
    <p class="s-forth">二、剩餘多行文案須要垂直居中剩餘空間</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),算是粘性佈局的一種。

看看效果:

margintopauto

嗯,這個需求若是可以使用 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;
}

Codepen Demo -- sticky footer by flex margin auto

上面的例子旨在介紹更多自動 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 訂閱收藏。

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

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

相關文章
相關標籤/搜索