CSS 排版與正常流 —— 重學CSS

2020-10-15 12:00:27

同學們好,我是來自 《技術銀河》的 💎 三鑽

這一週我們重新回到《重學 CSS》系列,之前的文章中我們重新學習了《 CSS 選擇器》和《 CSS 語法與規則》。接下來我們就一起來講講 CSS 裡面的排版與正常流。

在講解 CSS 當中的排版和正常流的時候,我們會按照屬性的一些邏輯關係來分成幾個部分來講解與學習。

盒 ( Box )

講到排版,我們需要引入的第一個概念就是 「」。之前我們在《模擬瀏覽器》和之前的一些 CSS 的文章中都講到了排版相關的概念。

而我們真正去講到排版的時候,我們需要用到的單位一定就是 「」。

在真正進入詳細瞭解 「盒」 的概念之前,我們先來做認識一下 3 個比較容易混交的概念。

  1. 標籤 ( Tag ) —— 原始碼
  2. 元素 (Element) —— 語意
  3. 盒 ( Box ) —— 表現

HTML 程式碼中可以書寫開始標籤,結束標籤,和自封閉標籤

標籤是一個原始碼的概念,所以方式我們提到在 HTML 程式碼中寫的肯定都是標籤。

一對起止標籤,表示一個元素

元素是存在我們腦子裡的一個概念,它是語意領域的一個概念,所以一對起止標籤它一定是表示一個我們腦子裡面的概念。

DOM 樹中儲存的是元素和其他型別的節點 ( Node )。

DOM 樹中儲存的不全是元素,因為DOM 樹中儲存的東西叫節點 Node,所以元素只是是節點的一種。

比如說我們的文位元組點也是節點,但他並不是元素。再比如我們的註釋節點,它也是節點但是它也不是元素。當然還有 CDATA 節點,還有 processing-instruction,DTD等這些都是會存入 DOM 樹的,當時它們都並不是元素。

很多同學的理解,DOM 樹中儲存的都是元素,不過這樣也沒有錯。因為其他的節點相對來說都沒有那麼重要。

CSS 選擇器中的是元素

其實這裡還可以加一個 「或」,在《CSS 選擇器》中講到的,CSS 選擇器選中的是元素或者是偽元素

CSS 選擇器中的元素,在排版時可能產生多個

這個地方是大家需要注意到的一個概念,CSS 選擇器選中的元素,它不一定和盒是一一對應的關係。它有可能是一對多的關係的。但是有盒一般來說必定是有對應的元素的。我們不可能無中生有產生一個元素,即使是號稱是無中生有的偽元素也是依附於一個選中的元素產生的。

排版和渲染的基本單位是

在我們的《模擬瀏覽器》的實現過程中,我們的排版盒渲染都是直接拿元素當盒去用了。但是這是一個很粗糙的做法,在實際上我們很多元素都會產生多個盒。

比如說 inline 元素就會因為分行而產生多個盒。又比如說帶有偽元素,被偽元素選擇器選中的元素也會生成多個盒。所以我們排版盒渲染的基本單位都是盒

盒模型

既然我們講到盒,我們都會講到大名鼎鼎的 「盒模型」。我相信很多同學都知道盒模型,並且也學習過盒模型。但是也有很多同學可能沒有理清楚摸透這個概念,所以就會導致不知道什麼是盒模型,更不知道""這個概念是從何而來。

上面我們已經講清楚了盒是從,標籤到元素,到 CSS 選擇器到如何產生了盒。所以對盒的來龍去脈我們都很清楚了,所以這裡我們就可以開始詳細的去了解盒模型的概念。

盒模型是我們排版的時候所用的一種基本單位

盒模型中的"盒",不光是有一個寬和一個高。不像在《模擬瀏覽器》部分裡面那樣非常的好算,其實它還是挺複雜的。

盒模型是一個多層的結構,從裡面到外面分為:

  1. 最裡面就是content,也就是我們的內容
  2. content 到 border 之間有一個圈空白,這個圈叫做 padding,也就是內邊距
  3. Border 的外面又有一個圈空白叫 margin,也就是外邊距

  • padding 主要影響的是盒內的空間 —— 主要決定盒內的空間排布,也就是 content 區域的大小
  • margin 主要影響的是盒外的空間 —— 決定了盒周圍空白區域的大小

盒模型裡面的 寬 (width) 是有講究的,盒子的寬度是有可能被 box-sizing 屬性所影響的。最常見的兩個值就是:

content-box

設定的 width 屬性只包含 content 的內容的空間。也就是說:

盒子佔用的空間 = content 的大小 + padding 的大小 + border 的大小 + margin 的大小

怎麼聽起來就是一臉懵的感覺。😂

其實更接近人類的理解就是,我們在 CSS 中設定的 width屬性只對最裡面的 content的空間有效。其餘的 paddingbordermargin 都會疊加到盒子的佔用空間。

那這個為什麼程式設計師都說這種盒子是 「反人類」 的呢?如果很早起就接觸到 HTML 和 CSS 的同學應該都知道這麼一個讓人痛不欲生的場景:

在排版時候我們明明設定好這個盒子的寬度,但是最後加了 border 和 padding 就讓盒子 「變大」 了。所以最後我們要反過來重新計算 width 屬性來保證這個盒子是我們想要的寬度。

對!可能很多用習慣 box-sizing: border-box 的同學,早就忘記了這些痛苦的日子了。但是事實上這個設計理念就是有點反人類的。所以後面就打了一個修補程式來拯救我們程式設計師,加入了 border-box。從此之後程式設計師就又可以開心的敲程式碼了。

border-box

使用border-box,我們的 width 就包含了 padding 和 border 的尺寸了。回去看看我們盒模型的圖,我們可以看到border-box的黃色線括著的區域,它所佔據的空間和範圍。

這樣當我們給一個盒,padding 和 border 的時候,就不會影響我們給予盒子的 width。這樣我們就可以保證我們盒子在沒有 margin 的時候它所佔據的空間就是與我們 width 一致的。

呈現出來的效果就是 padding 和 border 都會往內擠壓空間,而不會影響盒子的寬度。

這裡我們就講完盒模型了,我們就發現它所影響的屬性就是 marginpaddingborderbox-sizing這幾個屬性。這些都是影響我們盒模型的總體尺寸,在排版中會影響著這個盒模型所佔據的範圍。

正常流

CSS 的排版其實是有三代的排版技術的:

  • 第一代就是正常流
  • 第二代就是基於 Flex 的排版
  • 第三代就是基於 Grid 的排版
  • 結合最近推出的 CSS Houdini,可能更接近的是 3.5 代,它是一種完全自由的,允許使用 JavaScript 干預的排版

目前主流都是在使用 flex 佈局。相比 flex,其實正常流並沒有變得更簡單,反而是更復雜了。

不過挺有意思的是,flex 它比前面的第一代的排版技術要簡單,比他後面一代的 grid 也簡單。個人認為 flex 是最簡單並且最容易理解的一代排版技術。

正常流呢,其實它能力最差,但是反而他的機制很複雜。

排版

如果我們看到上面這個圖,可能有一些同學知道是什麼,有一些同學完全不知道他在幹什麼。其實這個就是 80 年代印刷廠工人在進行排版工作。

這個傳統的排版技術,其實與我們現在網頁的layout是息息相關的。在很多文章中,我們會把layout翻譯成排版,有時候也會翻譯成佈局。但是我個人也覺得翻譯成排版是最貼切的。因為 CSS 當中的layout是源自於傳統的排版技術。

傳統的排版方式,我們需要先把字版放入一個一個字框裡面,按照文字的順序排列號,然後再把這些字框一個一個的排列進我們的排版框裡面。

所以所謂排版就給我們所有可見的東西放到正確的位置上去。而在 HTML 裡面,我們是有 「盒」 這樣一個東西,在 CSS 的排版裡我們只排兩樣東西

  1. 文字

一切 CSS 的排版,都不會逃出這文字這樣兩東西。所以我們的排版就是給每一個文字安排到正確的位置上,然後給每一個盒安排到正確的位置上。

在不考慮盒模型的情況下,我們需要關注的就是位置和尺寸,所以排版中並沒有什麼特殊的內容。

字排版

我們在進入正常流之前,我們一起來先思考一下我們寫字的時候是怎麼寫的。

其實我們寫字的時候,某種意義上講也是一個排版的過程。很多同學們小時候寫作文的時候都會用到上面這種有格子的稿紙。我們都記得這個作文稿紙上都是有一個一個的格子的,其實這相當於一個已經有了排版。

如果在我們稿紙上沒有格子呢,我們就要自己來決定每一行要寫多少個字,每一個字有多高,然後怎麼分段呢?等等這些問題,都是在我們小時候寫字時需要關注的。

那麼我們來總結一下寫字有哪些規則呢?

  • 從左到右書寫 —— 我們都是依次從左向右書寫作文的
  • 同一行寫的文字都是對齊的 —— 寫中文的時候我們是對齊到格子的頂和底,寫英文的時候,像 e,a 這些字母它就有一條基線要去對齊。所以小時候英文的書寫本都是四線本。
  • 一行寫滿了,就換到下一行

其實以上的這些規則也就是我們說的 「正常流 ( Normal Flow )」,所以正常流為什麼正常呢?因為正常流就是與我們平時書寫文字的習慣一致。無論是中文也好,英文也好,它們都是遵循這種自然的排版方式的。

有一些我們早期進入前端的同學,就會發現其實正常流裡面,有很多特別不正常的東西,特別的反直覺,反人類的東西。為什麼這麼奇怪的東西要叫正常流呢?

很負責任的告訴大家,其實一點都不奇怪。如果我們知道前端的 HTML 和 CSS 的排版概念都是源自於專業排版知識,而這些排版使用方式如果出現在我們的書籍裡面,我們都會覺得挺自然的。但是真正讓我們去理解這些知識,我們都會覺得很困難。

這個就可以追溯到 HTML 最早期整個的排版設計,都是從文字出版行業過來的專家所做的。所以它使用的思路都是那個時代的一個專業的思路。跟我們自然人腦子裡面的理解,可能就會有一些差異了。

正常流排版

接下來我們就正式進入正常流的排版講解。

正常流排版的整個過程,與實現 flex 的過程比較類似,有這幾個步驟:

  • 收集盒與文字進行
  • 計算盒與文字在行中的排布
  • 計算行與行之間的排布

我們發現其實這個與我們 flex 的排版非常的像。其實我們會發現所有的排版演演算法,基本上都是差不多的。不論是哪個軟體,哪個規則,它們都是這麼幾個步驟。

接下來我們來看看具體的一個排布規則:

當文字和盒在一行裡面的時候,它們是會從左向右排布的。假設我們先不考慮 writing-mode 的事情,這裡所說的從左向右排就意味著文字和盒有一個對齊的規則。這個就比我們小時候寫作文的那個格子要複雜了。

在寫作文的格子稿紙上,我們是不需要考慮圖片和其他元素與我們的文字混排的情況的。我們在寫作文的時候不會寫著寫著在旁邊話一個插圖什麼的。但是我們發現書裡面是有的,特別是專業的書籍基本上都是圖文內容。

這些書裡面都是寫著寫著就會插入一個圖表,這些圖表有的是在行內放一個小的圖片,有的是裡面加一個小圖示。這些都是行內盒,更準確的說這叫 inline-level-box,就是行內級別的盒。

還有一些我們需要插入一個大圖,比如說一個統計的資料,因為這種一般來說高度都比較高,放在一行裡面會很奇怪。所以這個時候我們會讓它單獨佔一行。這種型別的盒,我們就把它稱為 block-level-box ,也叫塊級盒。

所以文字和 inline level 行內的盒排出來的行,我們就叫做 「行盒 ( line-box )」,然後一個自然的正常流,整體來看就是一個個的 line-boxblock-level-box 的從上到下的擺佈。如果沒有 block-level-box,那麼都是行盒從上到下排布。而每個行盒的內部都是從左到右的排布。

其實上面講到的兩種情況都是有一個名字的。

  • 塊級排布的我們就叫 BFC —— Block level formatting context (塊級格式化上下文)
  • 行內排布的我們就叫 IFC —— Inline level formatting context (行內級格式化上下文)

很多在比較早期學習前端,甚至在一些 「0 基礎」 的班裡面學前端的同學,都會聽說過 「塊級元素」 和 「行內元素」 兩種說法。其實與我們講到的 BFC 和 IFC 就是這個概念最原本的意思。

行級排布

小時候我們在學習英文的時候,就會使用過這種 4 線本的格式來書寫英文。

我們可以注意到在書寫 example 這個英文的時候,我們可以看到 exam 都是在一行之內的。但是 pl 就不一樣了。p 的尾巴是稍微往下突出的,而 l 它的頭部是稍微往上突出的。

所以英文的字母呢,總是會有一些字母會往上或者往下伸出一塊的。這個也是英文書寫的一些規範,因為遵循這些規範才能讓我們的字母在書寫的時候好看。基本上所有字母的下緣都是需要對準一條線的,那就是倒數第二條錢(在圖片中的黃色的線)。

這條線也叫 「基線」,而這個規則就叫做 「基線對齊」。

其實各國的文字都是有一條基線對齊的規則,但是不同的國家的文字依賴的基線的位置不一樣。比如說中文需要和英文混排的話,就會出現一個基線的偏移。

像中文這種型別的字,都會以上文下文這種邊緣作為基礎線去對齊的。但是我們也是可以認為這些型別的文字也是基於 基線 (baseline) 對齊的。我們可以理解為它們只是帶了一定的偏移。

字形定義

接下來我們一起來了解一下,一個字形它是怎麼樣去定義的。在之前的文章中我們有講到一些字元集這方面的基礎知識。其實一個字元就是一個碼點,以為著它的形狀是由我們的字型來決定的。

下面的兩張圖是來自於一個著名的 C++ 的底層庫,叫做 freeType。這個庫就是處理各種字型檔案成為抽象,並且它是一個開源界大部分的軟體都會去使用的一個字型庫。

這個庫中,它從字型中抽象出這麼一個定義:

任何一個文字,都是有一個寬和高,除此之外還有一條基線的定義。(如何沒有這個基線的定義,我們的字型是不成立的,也沒有辦法用它來進行排版)。這個定義就叫做 Glyph Metrics

我們先來看看第一張圖:

首先我們看到 origin,也就是一個字型的原點。而原點標識的位置就是我們文字的基線的位置,而這個文字都是以基線作為原點的座標,然後再用這個座標來定義這個文字的位置。

所以說基線就是在 x = 0 這個橫向的座標,然後這個文字就會有一個 xMax 和一個 yMaxxMax 就是文字最尾端的座標位置,而 yMax 就是文字最頂端的位置。

然後文字也有相反的兩個值 xMinyMin,而這兩個值就是剛好相反,是文字開始的位置和底部的位置。

也可以理解為從底緣的基線到文字的最頂端的距離,這就是 yMax。而底緣的基線到文字的最底端的距離就是 yMin。而 xMinxMax,從原點到文字開始和文字結束的距離。

最後這裡面的 advance 就是整個字佔用的空間。

以上講的就是橫向排版的時候,而縱排又有另外一套的邏輯:

我們需要理解一些文字這樣的基礎概念,然後我們才能更好的理解文字和盒的混排。

行模型

我們大致瞭解了這個文字在字型裡面是如何定義之後,我們就可以來講講 CSS 裡面的行模型了。這裡我們重點講 5 條線,其他還有一些線和位置例如 subsup 這種我們就不去講了。

這個 base-line 和文字的頂原和底緣,分別叫做 base-linetext-toptext-bottom。就是圖中的一條黃線和兩條綠線。

結合剛剛講到的,base-line 就是用來與英文字母對齊的。然後這裡面有一個 text-top 和一個 text-bottom,如果我們的字型大小不變,這兩個線是不會變得。

如果我們使用多個文字的字型混排的話,這個 text-toptext-bottom 是由 fontSize 最大的一個字型決定的。然後這個文字的上緣和下緣可以理解為兩條固定的線。

如果行高是大於文字的高度的時候,我們就會有 line-topline-bottom ,就是在圖中的兩條白色的線。

好!如果我們只有文字的話,這個行模型就這樣子排布的。但是我們一旦涉及到跟盒的混排,就會涉及到一個 line-topline-bottom 的偏移問題。

當我們的盒足夠大的時候,我們的盒子是從 text-bottom 去對齊的,所以它就有可能把這個高度撐開。這個時候 line-top 就會從虛線的位置,移動到了白色的實線的位置。(看上面的圖)

這個現象也是在正常流當中,處理行模型中非常麻煩的一個現象。因為盒的先後順序和尺寸都是影響 line-topline-bottom 的位置。但是盒是不會影響 text-toptext-bottom 的。

程式碼演示

接下來我們來一起看看在瀏覽器中的實際效果是怎麼樣,需要在瀏覽器上看效果,我們就需要些一段程式碼了。

  1. 我們先建立一個 wrapper 把我們的內容包裹起來,然後給一個背景顏色
  2. span 標籤加入文字
  3. 在用 div 標籤建立一個元素,然後給予這個元素 display: inline-block,把元素設定成行內盒
  4. 最後我們加入基線的元素 base-line,用這個來展示我們的基線是在哪個位置
<style>
  .wrapper {
    margin: 2rem;
    font-size: 50px;
    line-height: 100px;
    background-color: #2d2f42;
  }
  .base-line {
    overflow: visible;
    display: inline-block;
    width: 1px;
    height: 1px;
  }
  .base-line div {
    width: 1000px;
    height: 1px;
    background: fuchsia;
  }
  .text {
    color: #ddd;
  }
  .inline-box {
    line-height: 70px;
    width: 100px;
    height: 150px;
    background-color: aqua;
    display: inline-block;
  }
</style>

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 對齊基線 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字內容 -->
  <div class="inline-box"></div> <!-- 行級盒 -->
</div>

最後呈現出來的效果如下:

這裡我們發現,我們的行內盒是預設與基線對齊的規則。也就是說盒的下邊緣會和文字的基線去做對齊

好,如果我們在盒子裡面加文字又會怎麼樣呢?我們來試試看。

這裡我們在盒裡面加入一個 b 的文字。

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 對齊基線 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字內容 -->
  <div class="inline-box">b</div> <!-- 行級盒 -->
</div>

最後呈現出來的效果就是這樣的:

這裡我們會發現盒子的對齊的位置發生了變化。盒的基線變成了它裡面文字的最後一行的基線。也就是說,當一個盒子裡面有文字的時候,這個盒子的對齊就會基於裡面文字的基線做對齊。

這裡如果我們在 b 的下一行加一個文字 c,我們又會發現另一種現象。

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 對齊基線 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字內容 -->
  <div class="inline-box">b<br/>c</div> <!-- 行級盒 -->
</div>

這個是特別需要注意的問題,行內盒 inline-block 的基線是隨著自己裡面的文字去變化的。所以說大部分情況下是不建議大家給行內盒使用基線對齊的。

所以我們在使用行內盒的時候就需要給一個 vertical-align,屬性值我們可以給 topbottom或者是middle都是可以的。

我們先看看 vertical-align: top —— 頂緣對齊

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 對齊基線 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字內容 -->
  <div class="inline-box" style="vertical-align: top">b<br/>c</div> <!-- 行級盒 -->
</div>

top 就是跟行的頂緣對齊,因為我們的外包框給的是 100px,所以它會把這一行撐開。

然後我們來看看 vertical-align: bottom —— 底緣對齊

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 對齊基線 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字內容 -->
  <div class="inline-box" style="vertical-align: bottom">b<br/>c</div> <!-- 行級盒 -->
</div>

與頂緣對齊不一樣就是這個時候,盒就會從下面往上撐開。

最後我們看看 vertical-align: middle —— 中線對齊

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 對齊基線 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字內容 -->
  <div class="inline-box" style="vertical-align: middle">b<br/>c</div> <!-- 行級盒 -->
</div>

顧名思義,這個時候盒就會與文字的中線對齊。

其實這裡我們還可以讓盒與文字的頂緣和底緣對齊,也就是 text-toptext-bottom

我們來看看 text-bottom 是怎麼樣的:

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line"> <!-- 對齊基線 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字內容 -->
  <div class="inline-box" style="vertical-align: text-bottom">b<br/>c</div> <!-- 行級盒 -->
</div>

盒一樣會把上邊緣和下邊緣撐開。

如果我們看到上面的如,紫色的線就是我們的基線,但是我們也可看看文字的中心線頂緣線底緣線的位置。我們只需要改動 base-line 這個 div 元素的 vertical-align 屬性即可:

我們先看看中線:

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line" style="vertical-align: middle"> <!-- 對齊基線 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字內容 -->
  <div class="inline-box" style="vertical-align: text-bottom">b<br/>c</div> <!-- 行級盒 -->
</div>

頂緣線:

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line" style="vertical-align: top"> <!-- 對齊基線 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字內容 -->
  <div class="inline-box" style="vertical-align: text-bottom">b<br/>c</div> <!-- 行級盒 -->
</div>

底緣線:

<div class="wrapper"> <!-- 外包框 -->
  <div class="base-line" style="vertical-align: bottom"> <!-- 對齊基線 -->
    <div></div>
  </div>
  <span class="text">Hello world 中文</span> <!-- 文字內容 -->
  <div class="inline-box" style="vertical-align: text-bottom">b<br/>c</div> <!-- 行級盒 -->
</div>

這裡我們可以觀察到,我們的盒是把我們的 頂緣線 (top-line) 往上撐開了的,所以也證明我們行內盒是會把頂緣或者下緣撐開的。

為了讓大家可以更加直觀的看到頂緣和底緣被撐開的現象,我們可以加多一個行內盒,同時把頂緣和底緣同時撐開。

<div class="wrapper">
      <div class="base-line">
        <div></div>
      </div>
      <span class="text">Hello world 中文</span>
      <div class="inline-box" style="vertical-align: text-bottom">
        b
        <br />
        c
      </div>
      <div class="inline-box" style="vertical-align: text-top">
        b
        <br />
        c
      </div>
    </div>

這裡我們可以明顯發現,我們外包框的高度已經被撐開了,所以外包框的高度已經不是等於行內盒的高度了,應為它的頂緣和底緣線都被撐開了。這個顯現就是行內盒不同的 align 對齊所導致的,所以不同的 vertical-align 對整個行的高度是有比較多的影響的。所以相對於 flex 佈局,flex 就只需要去考慮最高的一個元素,但是在正常流裡面的行模型還是比較複雜的。

最後我給大家附上這段程式碼的演示:

檢視效果檢視程式碼喜歡的同學 🌟star 一下謝謝!

塊級排布

接下來我們來一起了解一下,正常流的塊級排布。我們在上一部分已經瞭解了塊級排布了。接下來我們一起來了解行級與塊級的盒之間是如何排布的。

之前講到的 BFC 和 IFC 的基本定義的時候,我們對塊級排布已經有一個基礎的認識了。但是呢正常流當中還有兩個非常複雜的機制,我們需要先了解一下。

Float 和 Clear

Float 和 Clear 也稱為 浮動清除浮動。首先浮動元素嚴格來說已經脫離了正常流,當時他又依附於正常流去定義的一類排布方式。

首先我們先來講一下 float 的基本規則。根據 W3C 的標準,float 可對它有一下定義:

float 元素可以先排到頁面的某一個特定的位置,同時可以當它是正常流裡的元素。然後如果它的屬性中有 float 的時候,這個元素就會朝著 float 屬性定義的方向去擠。

假設元素加入一個 float: left 屬性,這個時候,這個元素就會往左邊去擠,如下圖:

通過上面這個動畫,我們會發現,原來已經存在的文字位置就被浮動了的元素所 「蓋住」 了。所以呢這個時候就會根據 float 所佔據的區域去調整行盒的位置。因為計算位置的時候我們還沒有去計算每一個文字具體的位置,所以說理論上來講這個地方的文字是沒有重排的。

所以當一個元素變成了浮動時,它所佔據的位置的原本內容,就會根據 float 之後佔據的寬度來進行調整。而 float 顯著的特徵就是,它會影響我們生成這些行盒的尺寸。

所以在文字調整了之後我們最終看到瀏覽器呈現的效果是這樣的:

float 不止會影響自己所在的行,凡是它的高度所佔據的範圍內的所有行盒都會根據 float 元素的尺寸調整自己的大小。如果超出了這個 float 範圍的就不考慮了。

如果我們有兩個 float 元素的時候,還會出現一種情況。假設我們現在有兩個 float 元素,一個是 float: right 在右邊,然後再後面又加入了一個 float 元素,而這個新的 float 元素一樣也是向右浮動的。

這個時候我們會發現這個新的 float 元素也會受到上一個 float 元素影響,新的 float 元素是無法佔據上一個 float 元素的位置。這時候的這個現象就是 float 元素相堆疊的效果

當讓如果我們繼續新增 float 元素,並且也是浮動一樣的方向的時候,都會受到上面多個 float 元素的影響並且繼續堆疊。

接下來我們來看看一段範例程式碼:

<style>
  .float-box {
    float: right;
    width: 100px;
    height: 100px;
    background-color: fuchsia;
    color: #ededed;
  }
  .wrapper {
    padding: 1.5rem;
    background: #2d2f42;
    color: #ededed;
  }
  .purple {
    color: fuchsia;
  }
</style>

<div class="wrapper">
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
</div>

這裡面 「float-放置的位置」 就是原本這個 float 元素的程式碼所放置的位置。但是經過了 float 之後,所有的 float 元素都會被擠到最右邊的位置。最後所有行盒的生成都會圍繞著所有的 float 元素,同時它們都不會佔據 float 元素的空間。

這個時候當我們把外框的寬度增加的時候,就會發現這些 float 元素就會出現堆疊的現象。

看到這裡肯定很多同學說覺得 「這樣排版不行呀,老闆肯定不收貨呀」,「UI 設計師肯定要吊打我們啦」。是的這樣的排版確實是不正常的,而且一般來說也沒有這麼排版的需求呢。

所以這裡我們可以給 float 加一個 clear 屬性,這樣我們就可以讓它們強制換行了。

<style>
  .float-box {
    float: right;
    width: 100px;
    height: 100px;
    background-color: fuchsia;
    color: #ededed;
  }
  .wrapper {
    padding: 1.5rem;
    background: #2d2f42;
    color: #ededed;
  }
  .purple {
    color: fuchsia;
  }
</style>

<div class="wrapper">
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box" style="clear:right"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box" style="clear:right"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  <span class="purple">「float-放置的位置」</span>
  <div class="float-box"  style="clear:right"></div>
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
</div>

檢視效果檢視程式碼喜歡的同學 🌟star 一下謝謝!

所謂的 clear 有的翻譯成 「清除浮動」,但是我覺得它不是清除浮動的意思。理論上來說它是 「找一個乾淨的空間來執行浮動」 的意思。

比如說 clear: right,就是要在右邊找到一個乾淨的空間來執行這個浮動的操作。

所以看到上圖的效果,當我們加入了 clear: right 之後,第一個後面的元素都不受第一個 float 元素的影響,各自找到一個乾淨的空間進行浮動。所以 clear 是有分 leftright,指的是向左或者右找乾淨的空間進行浮動。

雖然說 float 這種浮動佈局給我們帶來了不少的麻煩,但是 float 的堆疊、自動換行(或者使用 clear 去換行)的行為是可以幫助我們去進行一些有用的佈局的。因為我們的正常流的佈局在早年沒有 flex的情況下,正常流的佈局下完成一些著名的 CSS 佈局需求的時候是非常的困難的。

所以大家都產生這種使用 float 來代替正常流的 inline-block 來進行佈局的技術

接下來我們看看下一段程式碼範例:

這裡我們給每一個元素都加上一個 float,這時候元素跟元素之間的表現就很像一個正常流了。因為 float 元素它佔滿了之後還是會換行的。那麼我們來看一下程式碼:

<style>
  .float-box {
    margin: 10px;
    float: left;
    width: 100px;
    height: 100px;
    background-color: fuchsia;
    color: #ededed;
    text-align: center;
    line-height: 100px;
    font-size: 50px;
  }
</style>

<div class="float-box">1</div>
<div class="float-box">2</div>
<div class="float-box">3</div>
<div class="float-box">4</div>
<div class="float-box">5</div>
<div class="float-box">6</div>
<div class="float-box">7</div>

這裡我們可以看到,float 是完全可以變成和正常流一樣排成一行的,當我們縮小寬度的時候,不夠位置的元素就會自動換到下一行。

[外連圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-UsH8GwbC-1602443824687)(/Users/tridiamond/Library/Application Support/typora-user-images/image-20201006194359630.png)]

這個時候如果我們想執行一個強制換行怎麼辦呢?這個時候我們就可以拿出我們的 clear。比如說我們想在 3 後面開始強制換行,我們就可以在 4 的浮動元素上加入 clear: left 即可。

這裡要注意的是,float 它是不認 <br/> 的,如果我們在一個 float 元素的後面加入 <br> 是無法讓他強制換行的。因為 br 是正常流的換行,對 float 是沒有效的。

<style>
  .float-box {
    margin: 10px;
    float: left;
    width: 100px;
    height: 100px;
    background-color: fuchsia;
    color: #ededed;
    text-align: center;
    line-height: 100px;
    font-size: 50px;
  }
</style>

<div class="float-box">1</div>
<div class="float-box">2</div>
<div class="float-box">3</div>
<div class="float-box" style="clear: left">4</div>
<div class="float-box">5</div>
<div class="float-box">6</div>
<div class="float-box">7</div>

我們平時做佈局的時候,基本上我們都會使用盒而不是文字。我們很少有真正意義上需要像原始的 float 設計一樣用圖文混排的設計(就是向我們看的圖書一樣的佈局方式)。在網頁上佈局,一般來說我們都是針對同一個級別,我們都是幾個盒子排成正確的形狀來完成頁面上的佈局。

所以說 float 這種佈局的技術,在比較早期的時代是非常流行的。不過到了現在的前端技術時代,float 的佈局方式已經完全被 Flexbox 技術所替代了。這裡只是讓大家知道一下,佈局的歷史裡面有這麼一個用法。

如果有同學遇到了一些需要維護非常古老的程式碼,就有可能看到這個技術。但是我不推薦大家當今還去使用這項技術。

檢視效果檢視程式碼喜歡的同學 🌟star 一下謝謝!

Margin 摺疊

接下來我們來講講正常流裡面的一種現象,叫做 margin 摺疊。在 BFC 裡我們的元素是順次從上往下排的,但是順次從上往下排的時候還是會受它的盒模型影響的。

就是有這麼一個現象,在一個從上往下排布的 BFC 裡面,有一個元素它有 margin,接著還有一個元素,它也有 margin。那麼這個時候第二個元素它應該怎麼排呢?

直接告訴我們應該是兩個元素的 margin 是應該會疊加,如果第一個的 margin 是 10,第二個是 15,那麼兩個元素中間就是 10 + 15 = 25。對吧?不是的。

它們並不是把兩個 margin 的空白都留出來。而是會讓他們兩個發生一個堆疊的這樣的現象。最後疊出來的高度是跟最大的 margin 的高度相等的。如果一個是 10px,一個是 15px 的 margin,最後兩個元素之間的空間就是 15px(使用了兩個 margin 的最大值)。這個現象就是 Margin Collapse (留白摺疊/邊距摺疊)

有些同學覺得真的是匪夷所思,根本不符合常理。但是其實這個是一個排版裡面的要求,因為在我們的排版當中,任何一個元素,它的盒模型裡面所謂的 margin 「只是要求周圍有這麼多的空間是留白的,而不會說要求元素與元素之間的邊距格子都有相對應的空白」。所以只要元素的周圍的留白的空間夠了,自然就是一個合理的排版方式。

這個可以說也是繼承了印刷行業,古老的印刷行業的排版體系中,這種 Margin Collapse 是一個非常自然的現象和思路。

我們要注意,這種 Margin Collapse 只會發生在 BFC 裡面。它不會發生在 IFC 或者其他的排版方式裡面,比如說 flex、grid 等都不會有 Margin Collapse 的。所以只有正常流中的 BFC 會發生邊距摺疊!

其實我們單看 float和單看邊距摺疊,它們都不太難,我們但看 BFC 也不太難。但是如果我們三個現象疊加在一起,基本上就是我們古代前端最大的難題了。也就是說大家平時會看到一些資料裡面講,面試的時候遇到的 BFC 問題就都在這個三個現象相疊加的。

BFC 合併

接下來我們來認識一下正常流最困難的一部分,"BFC 合併"。在講到具體的知識之前我們先來了解一下幾個概念。

BFC 代表的是 Block Formatting Context (塊級格式化上下文)

Block (塊)

我們先大致瞭解一下 Block 裡面有哪些:

  • Block Container —— 是在 CSS2.1 標準裡面定義的
    • 裡面能裝 BFC 的盒
    • 能容納正常流的盒,裡面就有 BFC
  • Block-level Box
    • 外面有 BFC 的盒
    • 也就是說它能夠被放入 BFC 的這種盒子裡
  • Block Box = Block Container + Block-level Box
    • 就是上面兩個之和
    • 裡外都有 BFC 的盒

這裡我們就逐個拆開來講一下:

Block Container

Block Containter 基本上是一些 display 的效果:

  • block
  • inline-block
  • table-cell —— 裡面都是正常流,但是 table-row 就不是 block cotainer 了,因為它裡面是 table-cell,所以不可能是正常流
  • flex item —— display: flex 的元素不是 block container,但是 flex 的子元素 flex item 如果它們沒有特殊的 display 屬性的話它們都是 block container。
  • grid cell —— grid 也是有 cell 的,所有 grid 的 cell 預設也都是 block container
  • table-caption —— table 中有 table-caption (表格標題),它裡面也是正常流

任何一個元素裡面只要不是特殊 display 模式的,它裡面預設就是正常流。

Block-level Box

我們大多數的元素的 display 的值都是有一對的。一個是 block-level 的,一個是 inline-level 的。

Block levelInline level
display: blockdisplay: inline-block
display: flexdisplay: inline-flex
display: griddisplay: inline-grid
display: tableDisplay:inline-table

在 display 的最新標準裡面,已經把 block-levelinline-level 拆成了單獨的屬性。Display 會出現這個 innerouter,這裡就是給 display 做了系統性的分解。

這裡還有一種非常特殊的 display 屬性叫 run-in。它跟著自己上一個元素來,有時候是 inline-level,有的時候是 block-level。這個屬性我們知道就可以了,在工作中基本上沒有見過有例子有運用到 run-in 這個屬性。

Block Box

在標準裡面有這麼一個描述:「什麼時候什麼樣的盒,會建立 BFC 呢?

設立 BFC (Establish BFC)

以下情況下會 設立 BFC

  • floats —— 浮動的元素裡面就是一個正常流,所以會建立 BFC
  • Abusolutely positioned elements —— 絕對定位的元素裡面也會建立 BFC
  • block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes —— 就是這個 block container 但是不是 block box (也就是不是 block-level ) 會建立 BFC,包括以下:
    • inline-blocks
    • table-cells
    • table-captions
    • Flex items
    • grid cell
    • 等等…
  • and block boxes with overflow other than visible —— 就是擁有 overflow 屬性,但是不是 visibleblock box 也會建立 BFC

所以設立 BFC 一共有 4 大類,但是要記住這些確實不太好記。不過換一種方式來理解就好記一點了。

預設這些能容納正常流的盒,我們都認為它們會建立 BFC,但是隻有一種情況例外:就是 block box 裡外都是 BFC 並且 overflow 是 visible。用公式來記就是這個:「block box && overflow:visible

這個其實是非常合理的,它裡外都是 BFC 而且它的 overflowvisible,那不就相當於沒有 BFC 了嗎?所以這個時候會發生 BFC 合併。

BFC 合併

那 BFC 合併之後會發生什麼呢?這裡我們就需要了解兩個最重要的 BFC 合併之後的影響:

BFC 合併與 float

因為 BFC 發生了合併,所以裡面的行盒跟這個 float就有了一定的影響。

正常的來講我們放一個 block box 它的 overflow 不是 visible,這個時候它會建立 BFC 並且整個 block box 放進 BFC 裡面。那麼整個就會受 BFC 影響,如果不建立 BFC它裡面的行盒就會受 float 的影響。

下來我們用一段程式碼來看看其中的現象:

<style>
  .float-box {
    float: right;
    width: 100px;
    height: 100px;
    background-color: aqua;
    margin: 20px;
  }
  .text {
    background-color: #2d2f42;
    color: #ededed;
    overflow: visible;
    margin: 30px;
  }
</style>

<div class="float-box"></div>
<div class="text">
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
</div>

這裡因為我們的 text 盒子給予了 overflow: visible,所以這個 overflow 屬性值是不滿足建立 BFC 的條件的。所以我們的文字的盒子就會像不存在一樣,文字就會環繞著外面的 float-box 元素來進行排布。

如果我們把 overflow 的屬性值改為 hidden 的話,那麼 text 的盒子就會建立 BFC。

<div class="float-box"></div>
<div class="text" style="overflow: hidden">
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
  文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字 文字
</div>

這裡我們就很明顯的可以看見一個變化,text 的元素整體作為一個 block level 的元素被排進了 BFC 裡面。也就是說這個時候 text 元素的寬度整個圍繞著 float-box 元素來進行排布了。

檢視效果檢視程式碼喜歡的同學 🌟star 一下謝謝!

BFC 合併與邊距摺疊

剛剛我們講到, BFC 只會發生在同一個 BFC 裡面。如果建立了新的 BFC 的話,它就不會發生邊距摺疊。如果沒有建立 BFC 的話,它就存在著一個同向邊距摺疊。

好,同樣我們用一段程式碼的範例來試驗一下:

<style>
  .box {
    width: 100px;
    height: 100px;
    background-color: aqua;
    margin: 20px;
  }
  .overflow-box {
    overflow: visible;
    background-color: #2d2f42;
  }
</style>

<div class="box"></div>
<div class="overflow-box">
  <div class="box"></div>
</div>

這裡的兩個 box 元素都給予了 100 x 100px 寬高,同時有一個 20px 的外邊距。第二個 box 元素我們用一個含有 overflow 屬性的 div 包著。首先我們預設給予這個 overflow 元素 overflow: visible,然後我們來看看會出現什麼效果。

出來的效果顯而易見,我們可以看到上面和下面的兩個 box 元素的外邊距發生了邊距摺疊現象。這裡兩個盒子就當做 overflow 這個元素不存在一般,最後他們兩個的距離就是 20px。

這裡做一個小實現,如果我們給 overflow 元素加入一個 margin-top: 30px,這個時候會對這兩個 box 的距離發生影響嗎?

答案是:會的!但是還是還是會出現 邊距摺疊 的現象,只是它們之間的距離變成了 30px,但是並不會變成 20+30+20 這樣的效果。我們來看看執行效果:

<div class="box"></div>
<div class="overflow-box" style="margin-top: 30px">
  <div class="box"></div>
</div>

最後如果我們把 overflow-boxoverflow 屬性改為 hidden, 這個時候它就會建立 BFC。這時候我們就會發現,邊距摺疊 現象就會消失,最終兩個 box 元素的距離就是 30+20 = 50px。

<div class="box"></div>
<div class="overflow-box" style="margin-top: 30px; overflow: hidden;">
  <div class="box"></div>
</div>

「不知道這裡發生了什麼,但是覺得很棒!哈哈」

這裡的 overflow-box 裡面的 box 元素已經和外面的 box 元素不在同一個 BFC 裡面了。所以它們之間不會再發生邊距摺疊現象。但是 外面的 overflow-boxbox 是處於同一個 BFC 中,所以它們兩個依然會發生邊距摺疊現象。所以這個就是建立 BFC 對邊距摺疊這個行為的影響。

我是來自《技術銀河》的三鑽:「學習是為了成長,成長是為了不退步。堅持才能成功,失敗只是因為沒有堅持。同學們加油哦!下期見!」


推薦專欄

小夥伴們可以檢視或者訂閱相關的專欄,從而集中閱讀相關知識的文章哦。

  • 📖 《前端進階》 — 這裡包含的文章學習內容需要我們擁有 1-2 年前端開發經驗後,選擇讓自己升級到高階前端工程師的學習內容(這裡學習的內容是對應阿里 P6 級別的內容)。

  • 📖 《資料結構與演演算法》 — 到了如今,如果想成為一個高階開發工程師或者進入大廠,不論崗位是前端、後端還是AI,演演算法都是重中之重。也無論我們需要進入的公司的崗位是否最後是做演演算法工程師,前提面試就需要考演演算法。

  • 📖 《FCC前端集訓營》 — 根據FreeCodeCamp的學習課程,一起深入淺出學習前端。穩固前端知識,一起在FreeCodeCamp獲得證書

  • 📖 《前端星球》 — 以實戰為線索,深入淺出前端多維度的知識點。內含有多方面的前端知識文章,帶領不懂前端的童鞋一起學習前端,在前端開發路上童鞋一起燃起心中那團火🔥

三鑽 CSDN認證部落格專家 前端 Vue React
—— 起步於PHP,一入前端深似海,最後愛上了前端。Vue、React使用者。專於Web、行動端開發。特別關注產品和UI設計。專心、專注、專研,與同學們一起終身學習。關注我的微信公眾號《技術銀河》有更多最新知識文章與同學們分享。