前言:接下來的幾篇系列文章我們講一個東西——盒子(BOX)。
「盒模型」(Box Model)作為 CSS 看待元素的一種方式,CSS 將每個元素都看作由一個盒子表示。從某方面來說,對於初級、中級學習者的我們,大可將 CSS 的學習看作是對「盒子」的學習。
本篇我們將闡述最基本的理論知識,將「盒子」的方方面面一步步帶到你的跟前。
1 什麼是「盒子」「盒子 box」由 CSS 引擎根據文檔中的內容所創建,主要用於文檔元素的格式化、定位和布局等。
盒子與元素並不是一一對應的,有時多個元素會合併生成一個盒子,有時一個元素會生成多個盒子(如匿名盒子)。
一個完整的「盒子」中心有一個內容區(content area)。這個內容區周圍有可選的 padding、邊框和 margin。這些項之所以被認為是可選的,是因為它們的寬度可以設置為 0,實際上就是從「盒子」上去除這些項。一個完整的「盒子」
2 「視覺格式化模型」概述CSS 視覺格式化模型(Visual formatting model)是用來處理和在視覺媒體上顯示文檔時使用的計算規則。
通俗地講就是:頁面(文檔樹)可以想像成是由一個個 box 組合而成的,而「視覺格式化模型(Visual formatting model)」是一套規則,將這些 box「布局」成訪問者看到的樣子。
每個盒子的「布局」由以下因素決定(本篇文章和下一篇文章主要講解第 ①、② 點,其屬於「最基本的視覺格式化」,而對於剩下的要點,我們在接下來的系列文章中會挨個討論):
① 盒子的尺寸:精確指定、由約束條件指定或沒有指定;
② 盒子的類型:行內盒子(inline box)、行內級盒子(inline-level box)、原子行內級盒子(atomic inline-level box)、塊盒子(block box);
③ 定位方案(positioning scheme):普通流定位、浮動定位或絕對定位;
④ 文檔樹中的其他元素:即當前盒子的子元素或兄弟元素;
⑤ 視口尺寸與位置;
⑥ 所包含的圖片的尺寸;
⑦ 其他的某些外部因素。
每一個元素都是一個「盒子」,「盒子」可以嵌套「盒子」。
💡如上圖所示,視覺格式化模型會根據盒子的「包含塊」(containing block)——(包含其他盒子的塊稱為「包含塊」)的邊界來渲染盒子。通常,盒子會創建一個包含其後代元素的「包含塊」,但是盒子並不由「包含塊」所限制,當盒子的布局跑到「包含塊」的外面時稱為溢出(overflow)。
上圖中,section 的包含塊是 body,header、article、footer 的包含塊是 section。
❗️區別:
3 關於「盒子」我們一定需要知道的——基本視覺格式化3.1 「盒子」的生成盒子的生成是 CSS「視覺格式化模型」的一部分,用於從文檔元素生成盒子。
盒子有不同的類型,盒子的類型取決於 CSS 的 display 屬性——元素「角色」的改變。
設置元素的 display 屬性為 block、list-item 或 table 時,該元素將成為「塊級元素」。
這些元素在正常流中時,會在其框之前和之後生成「換行」,所以處於正常流中的塊級元素會「垂直」擺放。選擇器 {
display: block、list-item 或 table;
}
💡(「正常流」是指:西方語言文本從左向右、從上向下顯示的方向,這也是我們熟悉的傳統 HTML 文檔的文本布局方向。注意,在非西方語言中,流方向可能不同。)
但,元素是否是「塊級元素」僅是元素本身的屬性,並不直接用于格式化上下文的創建或布局。
一個「塊級元素」會被格式化成一個塊(例如文章的一個段落),且默認按照垂直方向依次排列。
💡一個「塊級元素」都會至少生成一個「塊級盒子」,也有可能生成多個(例如列表項元素)。而「塊級盒子」才會參與「塊格式化上下文(block formatting context)」的創建。
3.1.2 「行內級元素」和「行內盒子」當元素的 display 屬性為 inline、inline-block 或 inline-table 時,該元素將成為「行內級元素」。選擇器 {
display: inline、inline-block 或 inline-table;
}
這些元素不會在之前或之後生成「行分隔符」,所以處於正常流中的塊級元素會「水平」擺放,它們是塊級元素的後代。
顯示時,它不會生成內容塊,但是可以與其他行內級內容一起顯示為多行。
💡同理,「行內級元素」會生成「行內級盒子」,該盒子同時會參與「行內格式化上下文(inline formatting context)」的創建。
❗️注意:一定要記得的是,display 之所以得名,是因為它影響的是元素如何「顯示」,而不影響它本質上是何種元素,也就不能亂玩「嵌套關係」!<a href="http://…" style="display: block;">
<p style="display: inline;">這是一個錯誤的示例</p>
</a>
本篇我們主要探討 「塊盒子」格式化,下篇文章討論「行內盒子」格式化。
塊盒子
3.2.1 水平格式化正常流中,「塊盒子」的水平部分 = 其父元素的 width = 7 個屬性之和——(margin-left ➕ margin-right) ➕ (padding-left ➕ padding-right) ➕ (border-left ➕ border-right) ➕ 內容區自身 width。
在這 7 個屬性中只有 3 個屬性的值可以設置為 auto:width、margin-left、margin-right。其餘的要不必須是確定的值,要不就是默認值 0。🚀可詳細分為以下 5 種組合:
3 個都不是 auto:按 CSS 的術語來講,這叫「格式化屬性過分受限」,此時總會把 margin-right 強制為 auto 來適應父元素的寬度;
2 個不是 auto:是 auto 的將自動調整到適應父級元素的寬度;
margin-left、margin-right 是 auto:它們會自動設置為相等的長度,導致此元素在其父級元素中居中;
一個 margin 和 width 是 auto:auto 的 margin 會減為 0,width 自動填充其包含塊;
3 個都是 auto:兩個外邊距減為 0,width 會儘可能的寬(自動充滿)。
❗️注意:由於水平 margin 不會合並,父元素的 padding、邊框、margin 可能會對子元素帶來「偏移」的影響。
3.2.2 垂直格式化正常流中,「塊盒子」的垂直部分 = 其父元素的 height = 7 個屬性之和——(margin-top ➕ margin-bottom) ➕ (padding-top ➕ padding-bottom) ➕ (border-top ➕ border-bottom) ➕ 內容區自身 height。
同理,在這 7 個屬性中只有 3 個的值可以設置為 auto:height、margin-top、margin-bottom。其餘的要不必須是確定的值,要不就是默認值 0。
❗️不過,margin-top 和 margin-bottom 設置為 auto 也沒有什麼用,因為會被重置為 0。所以,想利用上下 margin 都是 auto 來垂直居中是不可能的。垂直格式化的另一個重要方面是垂直相鄰 margin 的合併。這種合併行為只應用於 margin,如果元素有 padding 和邊框,padding 和邊框是不會合併的。當兩個或更多垂直 margin 相遇時,它們將形成唯一一個 margin,這個 margin 的高度等於兩個發生疊加的 margin 的高度中的較大者。
❗️注意:當一個元素包含在另一個元素中時,彼此相鄰的 margin-bottom 和 magin-top 也會發生疊加,取較大者。
3.2.3 負 margin🤔提問:水平或垂直方向各自 7 大屬性相加要等於父元素的 width 或 height,那 margin 為負值會造成什麼結果?
1. 水平方向<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div>
<p class="wide">How are you?</p>
<p>Fine,thank you,and you?</p>
</div>
</body>
</html>
div {
width: 400px;
border: 3px solid black;
}
p.wide {
border: 1px dashed black;
margin-left: 20px;
width: auto;
margin-right: -50px;
background-color: yellow;
}
div {
width: 400px;
border: 3px solid black;
}
p.wide {
border: 1px dashed black;
margin-left: 20px;
width: 500px;
margin-right: auto;
background-color: yellow;
}
div {
width: 400px;
border: 3px solid black;
}
p.wide {
border: 1px dashed black;
margin-left: -50px;
width: auto;
margin-right: 10px;
background-color: yellow;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div>
<p class="wide1">How are you?</p>
<p class="wide2">Fine,thank you,and you?</p>
</div>
</body>
</html>
div {
width: 400px;
border: 3px solid black;
}
p.wide1 {
border: 1px dashed black;
margin-top: -20px; /* 修改這裡的值來觀察效果 */
margin-right: 20px;
margin-bottom: 30px;
margin-left: 20px;
width: auto;
background-color: yellow;
}
p.wide2 {
border: 1px dashed black;
margin-top: ;
margin-right: 20px;
margin-bottom: ;
margin-left: 20px;
width: auto;
background-color: grey;
}
div {
width: 400px;
border: 3px solid black;
}
p.wide1 {
border: 1px dashed black;
margin-top: px;
margin-right: 20px;
margin-bottom: -50px; /* 修改這裡的值來觀察效果 */
margin-left: 20px;
width: auto;
background-color: yellow;
}
p.wide2 {
border: 1px dashed black;
margin-top: ;
margin-right: 20px;
margin-bottom: ;
margin-left: 20px;
width: auto;
background-color: grey;
}
💡把段落 1 的 margin-bottom 設為「負」,段落 2 會根據段落 1 底端的位置放置。
「塊盒子」重疊的時候,如果垂直 margin 都為負值,瀏覽器會取兩個 margin 絕對值的最大值;如果一個正 margin 與一個負 margin 合併,則會從正 margin 減去這個負 margin 的絕對值。
🔗效果及源碼連結<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div>
<ul>
<li>Fine</li>
<li>thank you</li>
<li>and you?</li>
</ul>
<p>回答 How are you? 的方式!</p>
</div>
</body>
</html>
div {
width: 400px;
border: 3px solid black;
}
ul {
border: 1px dashed black;
margin-top: px;
margin-right: 20px;
margin-bottom: -15px; /* ① 修改這裡的值來觀察效果; */
margin-left: 20px;
width: auto;
background-color: yellow;
}
li {
border: 1px dashed black;
margin-top: ;
margin-right: 20px;
margin-bottom: 20px; /* ② 修改這裡的值來觀察效果。 */
margin-left: 20px;
width: auto;
background-color: grey;
}
p {
border: 1px dashed black;
margin-top: -18px; /* ① 修改這裡的值來觀察效果。 */
margin-right: 20px;
margin-bottom: px;
margin-left: 20px;
width: auto;
background-color: yellow;
}
💡當上邊的較大者(-18px)增加到 li 的最大正 margin 上(20px)時,就得到了 20px - 18px = 2px。
後記:這篇我們學習了「塊盒子」的格式化方式,下一篇我們接著這篇繼續探討「行內盒子」的格式化方式。在「行內盒子」格式化方式中,我們會談到很多細小的基礎知識,和本篇的學習方式一樣,讓我們儘可能地用代碼、用圖片來攻克它們。