從CSS盒子模型說起

Damonare發表於2017-07-12

前言

總括: 對於盒子模型,BFC,IFC和外邊距合併等概念和問題的總結

為學之道,莫先於窮理;窮理之要,必先於讀書。

正文

​ CSS盒子模型是CSS基礎中的基礎,個人之前對於這塊的理解有偏差?,由於涉及知識點比較多所以寫一篇總結備忘。
之前打算的是兩週一次更新博文的,但是時間用在了刷題上,做了很多leetcode上演算法資料結構的題記錄在github,但其實也有在更新啦~只不過是對之前的一些博文進行糾正:)?

​ 最近秋招在即,壓力倍增,前幾天把部落格匯入頁的在讀大三本科生改為了在讀大四本科生,不禁心生感慨,時光荏苒。轉眼間我的這個小窩竟然已經快一年了。當初建立這個站點也是想找個說話的地方,有的人可能就是不喜歡說,只喜歡寫(比如我),然而自從實習後確實提不起精神來寫了,一是沒精力,二是對於遇到的一些坑不想單獨寫一篇部落格記錄。這裡還是想保持一份純淨,就是以總結和理解難點為主調 其它的一般會託管在github庫裡記錄一下。閒話不多說,說說今天的主角?‍♀️

​ CSS盒子模型想來都不陌生,但還是想先介紹一下,以保證文章的完整性。?

盒子模型

​ CSS盒子模型:

盒子模型
盒子模型

在一個文件中,每一個元素都被抽象成一個盒子,每一個盒子又包括四部分(從內到外):內容(content),內填充(padding),邊框(border),外邊距(margin)。見上圖,這是從二維的角度分析,來張三維立體圖:?

網上找的圖片
網上找的圖片

此圖很形象的解釋了CSS盒子的構成:

  1. content box:立體盒子的核心
  2. padding box:內邊距區域padding area 延伸到包圍padding的邊框。如果內容區域content area設定了背景、顏色或者圖片,這些樣式將會延伸到padding上(當然我們可以通過background-clip設定作用區域)
  3. border box:由border和4條border edge組成。若border寬度設定為0,則border edge與padding edage重疊;
  4. margin box:由margin和4條margin edge組成。若margin寬度設定為0,則margin edge與border edage重疊。

?看起來很複雜的樣子...

拿PS圖層的概念更好理解這塊,最上面的就是content box往下一次是padding box,border box,margin box。

那麼盒子模型一般分為兩種:

IE盒子模型

所謂IE盒子模型,就是之前IE瀏覽器實現的一種怪異的盒子模型,怎麼怪異呢?當我們這樣設定的時候:

div {
    width: 100px;
      height: 100px;
}複製程式碼

理論上我們想要設定的就是content box的寬高嘛,但是IE在解析的時候會按照這個規則解析:

width = content-width + padding-width + border-width
height = content-height + padding-height + border-height

這就導致了這種尷尬的境地:下面無內容的話直接戳這裡?

標準盒子模型

標準就比較符合常人的思維了,設定的width,height就是content的width和height

規則就是:

width = content-width

height = content-height

例項如下:無內容戳這?

可能秉著寬大為懷的準則,CSS3加了個box-sizing屬性,變相承認了這兩種盒子都對(好吧,可能一個人有一個人的看法吧),不過box-sizing預設屬性就是content-box,即標準盒子模式,IE盒子模型呢,是屬性border-box。剛剛查MDN發現還有一個屬性padding-box(width=content-width+padding-width),不過並沒有瀏覽器實現它(真可憐),並無卵用?

行內元素的思考

剛剛說的是以塊級元素為例說的,那麼行內元素呢?好吧,其實你知道,行內盒是沒法設定width和height的,那麼之前我就有了這樣的思維定勢:行內盒沒有padding,margin,然後發現,哦!行內盒是有padding-left,padding-right,margin-left,margin-right的!WOC!,然後又發現,行內盒是實際上身懷八甲...?

以下無內容戳這裡

行內盒子的高由font-size決定的;
行內盒子的寬等於其子行級盒子的外寬度(margin+border+padding+content width)之和。

是有padding-top和padding-bottom,margin-left,margin-bottom的但並不佔據空間…這就符合盒子模型了嘛,既都是盒子,自然應該是一樣的。行內盒的margin-top, margin-bottom不佔空間,由此聯想到了另一個問題——?

外邊距合併

所謂外邊距合併呢,就是margin合併嘛,看下MDN的定義:

塊的頂部外邊距和底部外邊距有時被組合(摺疊)為單個外邊距,其大小是組合到其中的最大外邊距,這種行為稱為外邊距合併

?注意只是上下,沒有說左右。而且是針對塊級元素說的。

外邊距合併有這幾種情況:

相鄰兄弟元素

//HTML
<div class="up">我在上面</div>
<div class="down">我在下面</div>
//CSS
.up {
  width: 100px;
  height: 100px;
  border: 1px solid blue;
  margin: 100px;
}
.down {
  width: 100px;
  height: 100px;
  border: 1px solid red;
  margin: 100px;
}複製程式碼

我們感性上覺得上下兩個元素應該是相差200px距離,然而並不是。

父子元素

如果塊級父元素中,不存在上邊框、上內補、inline content、清除浮動這四條屬性(對於上邊框和上內補,也可以說,當上邊距及上內補寬度為0時),那麼這個塊級元素和其第一個子元素的上邊距就可以說”捱到了一起“。此時這個塊級父元素和其第一個子元素就會發生 上外邊距合併 現象,換句話說,此時這個父元素對外展現出來的外邊距將直接變成這個父元素和其第一個子元素的margin-top的較大者。?

//HTML
<div class="parent">
  <div class="child">我是兒子</div>
</div>
//CSS
.parent {
  width: 100px;
  height: 200px;
  background: red;
  margin-left: 100px;
}
.child {
  width: 50px;
  height: 50px;
  margin-top: 100px;
  border: 1px solid blue;
}複製程式碼

上面程式碼感性上可能會覺得,父元素沒有上邊距,然而並不是。

MDN給了三種情況,但第三種空塊元素,我覺得可以包含在這兩種之內,就沒舉?

那麼這種外邊距合併的情況咋解決呢?看下一個概念...

BFC

?定義:

一個塊格式化上下文(block formatting context) 是Web頁面的視覺化CSS渲染的一部分。它是塊盒子的佈局發生,浮動互相互動的區域。

那麼觸發BFC的情況有哪些呢?

看MDN:

?一個塊格式化上下文由以下之一建立:

  • 根元素或其它包含它的元素
  • 浮動 (元素的`[float](https://developer.mozilla.org/zh-CN/docs/Web/CSS/float) 不是none`)
  • 絕對定位的元素 (元素具有 positionabsolutefixed)
  • 內聯塊 inline-blocks (元素具有 display: inline-block)
  • 表格單元格 (元素具有 display: table-cell,HTML表格單元格預設屬性)
  • 表格標題 (元素具有 display: table-caption, HTML表格標題預設屬性)
  • 塊元素具有overflow ,且值不是 visible
  • display: flow-root

注意,根元素就建立了一個BFC

那麼BFC又有一下特性:

  1. 內部塊級盒子垂直方向排列
  2. 盒子垂直距離由margin決定,同一個BFC的盒子外邊距會合並
  3. BFC就是一個隔離的容器,內部子元素不會影響到外部元素
  4. 每個元素的margin box的左邊, 與包含塊border box的左邊相接觸(對於從左往右的格式化,否則相反)。即使存在浮動也是如此。
  5. BFC的區域不會與float box疊加。

好,上面外邊距合併的兩種情況,利用BFC如何解決呢?下面沒內容的話請戳這裡?

關於第四五條特性,請看上面的示例。

BFC用途:

  1. 清除浮動;
  2. 解決外邊距合併;
  3. 佈局;

塊級盒子的概念

關於這塊有好多個概念...首先是塊級元素和塊級盒子:每個塊級元素至少生成一個塊級盒,稱為主要塊級盒。一些元素,比如li,生成額外的盒來放置專案符號,不過多數元素只生成一個主要塊級盒。

主要塊級盒將包含後代元素生成的盒以及生成的內容。它也是可以使用(定位方案 positioning scheme)的盒。

塊容器盒(block container box) 只包含其它塊級盒,或生成一個行內格式化上下文(inline formatting context)

注意塊級盒與塊容器盒概念不同。 前者描述元素跟它的父元素與兄弟元素之間的表現,後者描述元素跟它的後代之間的影響。

同時是塊容器盒的塊級盒稱為塊盒(block boxes)。(注意塊盒和塊級盒並不是全等)

還有一個特殊的塊盒——匿名塊盒

<div>Some inline text <p>followed by a paragraph</p> followed by more inline text.</div>
//將建立兩個匿名塊盒,一個包含 <p> 前面的文字 (Some inline text), 一個包含 <p> 後面的文字(followed by more inline text),複製程式碼

塊級元素觸發BFC,行內元素會觸發啥麼❓

IFC

IFC 只有在一個塊級元素中僅包含內聯級別元素時才會生成。

行內盒子的概念

當元素的 CSS 屬性 display的計算值為 inline, inline-blockinline-table時,稱它為行內級元素。視覺上它將內容與其它行內級元素排列為多行。典型的如段落內容,有文字(可以有多種格式譬如著重),或圖片,都是行內級元素。

行內級元素生成行內級盒(inline-level boxes),參與行內格式化上下文(inline formatting context)。同時參與生成行內格式化上下文的行內級盒稱為行內盒(Inline boxes)。所有display:inline 的非替換元素生成的盒是行內盒。而不參與生成行內格式化上下文的行內級盒稱為原子行內級盒(*atomic inline-level boxes)。這些盒由可替換行內元素,或 display 值為 inline-blockinline-table的元素生成,不能拆分成多個盒。

另外CSS3還新增了兩種格式上下文:GFC(Grid Formatting Contexts)柵格格式化上下文和FFC(Flex Formatting Contexts)Flex格式化上下文,即分別在元素display為grid和flex、 inline-flex 時觸發

定位

常規流

分清了這些盒子的概念,具體怎麼排列呢?以下來自MDN:

在常規流中,盒一個接著一個排列。在塊級格式化上下文裡面, 它們豎著排列;在行內格式化上下文裡面, 它們橫著排列。 當 position為 staticrelative,並且 float 為 none 時會觸發常規流。

浮動(Floats)

對於浮動定位方案(float positioning scheme), 盒稱為浮動盒(floating boxes)。它位於當前行的開頭或末尾。這導致常規流環繞在它的周邊,除非設定 clear 屬性。

要使用浮動定位方案,元素 CSS 屬性position 為 staticrelative,然後float不為none 。如果 float設為 left, 浮動由行盒的開頭開始定位。如果設為 right, 浮動定位在行盒的末尾。

絕對定位(Absolute positioning)

對於絕對定位方案, 盒從常規流中被移除,不影響常規流的佈局。 它的定位相對於它的包含塊,相關CSS屬性:top, bottom, left及right 。

如果元素的屬性position為 absolutefixed, 它是絕對定位元素。

固定定位元素(fixed positioned element)也是絕對定位元素,它的包含塊是視口。當頁面滾動時它固定在螢幕上,因為視口沒有移動。

以上。

後記

參考文章

外邊距塌陷

Box model

Block formatting context

視覺格式化模型

相關文章