Learning Objectives
- 說出盒模型由內而外的四層:content、padding、border、margin,以及哪幾層會顯示背景
- 分辨 box-sizing 的 content-box 與 border-box,算出元素實際佔的寬度
- 說明為什麼常在全站設定 box-sizing: border-box
- 預測上下相鄰區塊的外距合併結果,並用 margin: 0 auto 讓區塊水平置中
- 分辨 display 的 block、inline、inline-block,說明 inline 元素為什麼設了 width 沒有作用
網頁上的每個元素都是一個盒子
看一張社團介紹卡片:文字離卡片邊緣留了一點空白、卡片有一圈框線、兩張卡片之間隔開一段距離。 這三種「距離」在 CSS 裡是三個不同的屬性,而且會影響卡片最後有多寬。
瀏覽器排版時,把每一個元素都當成一個矩形的盒子:段落是盒子、標題是盒子、連結也是盒子。 排版的工作就是決定每個盒子多大、放在哪裡。這套規則叫做盒模型(box model)。
很多排版上的疑問都出在盒模型:
| 常見的狀況 | 原因(這一課會講) |
|---|---|
設了 width: 300px,量起來卻比 300px 寬 |
width 預設只算內容,padding 與 border 另外加上去 |
| 兩個段落各有 20px 的外距,中間卻只隔 20px | 上下相鄰的外距會合併 |
給 <span> 設寬度,完全沒有反應 |
span 是 inline 元素,width 對它沒有作用 |
打開瀏覽器的開發者工具(第 1 課),選一個元素,「Computed」分頁就會畫出它的盒模型圖,數字和這一課的工具一樣。
盒子的四層:content、padding、border、margin
一個盒子由內而外分成四層:
| 層 | 中文 | 是什麼 | 有沒有背景色 |
|---|---|---|---|
| content | 內容區 | 文字、圖片所在的區域,width、height 預設就是設定這一層 |
有 |
| padding | 內距 | 內容與邊框之間的空白 | 有(和內容同一個背景) |
| border | 邊框 | 盒子的框線 | 框線自己的顏色 |
| margin | 外距 | 盒子與旁邊其他盒子之間的距離 | 沒有,永遠是透明的 |
.card {
width: 300px; /* content 的寬度 */
padding: 20px; /* 四邊的內距 */
border: 2px solid #3C6F9A; /* 粗細、樣式、顏色 */
margin: 16px 0; /* 上下 16px、左右 0 */
}
padding 與 margin 可以寫一到四個值,四個值的順序是上、右、下、左(從上面開始順時針):
| 寫法 | 意思 |
|---|---|
margin: 10px; |
四邊都是 10px |
margin: 10px 20px; |
上下 10px、左右 20px |
margin: 10px 20px 30px; |
上 10px、左右 20px、下 30px |
margin: 10px 20px 30px 40px; |
上、右、下、左 |
margin-top: 10px; |
只設定一邊(-right、-bottom、-left 同理) |
想讓兩個元素離遠一點用 margin;想讓文字離框線遠一點用 padding。分不清楚時看背景:padding 會被背景色填滿,margin 不會。
width 算到哪裡:content-box 與 border-box
box-sizing 決定 width、height 指的是哪一層:
| box-sizing | width 指的是 | width: 300px; padding: 20px; border: 2px 的實際寬度 |
|---|---|---|
content-box(預設值) |
只有 content | 2 + 20 + 300 + 20 + 2 = 344px |
border-box |
content + padding + border | 300px(content 自動縮成 300 − 44 = 256px) |
兩種都不含 margin;margin 永遠在 width 之外。
預設的 content-box 讓寬度很難掌握:你想要一張 300px 寬的卡片,每次改 padding 或 border,卡片就跟著變寬。 所以很多網站會在 CSS 的一開始就把所有元素改成 border-box:
*, *::before, *::after {
box-sizing: border-box;
}
* 選中所有元素;::before、::after 是虛擬元素(第 10 課),也一起改。
這樣寫完之後,width: 300px 就是畫面上看到的 300px,調整 padding 只會擠壓內容,不會撐大盒子。
box-sizing 不會繼承,所以要用 * 每個元素都設定。
上下外距合併與 margin: 0 auto 置中
外距合併(margin collapsing):一般的區塊排列中,上下相鄰的兩個外距不會相加,而是合併成比較大的那一個。
h2 { margin-bottom: 30px; }
p { margin-top: 20px; }
/* h2 與 p 之間的距離是 30px,不是 50px */
幾個要記的細節:
- 只有上下方向會合併,左右的外距一定相加。
- 父元素沒有 padding、border 隔開時,第一個子元素的 margin-top 會「穿出」父元素,和父元素的外距合併。常見的症狀是:子元素設了 margin-top,結果整個父元素往下移。給父元素加一點 padding 或 border 就不會穿出。
- Flex、Grid 容器裡的項目、浮動(float)與絕對定位的元素,外距都不會合併(第 15、16、17 課)。
水平置中:一個有設定寬度的區塊,左右外距都設成 auto,瀏覽器會把剩下的空間平分到左右兩邊:
.page {
max-width: 960px; /* 一定要有寬度,否則區塊本來就佔滿整行,沒有剩下的空間可以分 */
margin: 0 auto; /* 上下 0、左右 auto */
}
這是讓整個頁面內容在寬螢幕上置中最常見的寫法。注意它只能水平置中;垂直置中要等 Flexbox(第 17 課)。
小測驗:盒子的寬度與外距
.box { width: 200px; padding: 20px; border: 5px solid; },box-sizing 是預設值。這個盒子在畫面上的寬度(不含 margin)是多少?
同一個盒子改成 box-sizing: border-box,內容區(content)的寬度變成多少?
上下相鄰的兩個段落,上面的 margin-bottom: 30px,下面的 margin-top: 20px。兩段之間實際隔多遠?
對一個 <div> 寫了 margin: 0 auto;,它卻沒有置中。最可能的原因是?
display:block、inline 與 inline-block
盒子怎麼排列,由 display 決定。每個元素都有預設值:
| display | 預設是這個值的元素 | 排列方式 | width、height | 上下的 margin |
|---|---|---|---|---|
block(區塊) |
div、p、h1~h6、ul、li、section |
自己佔一整行,寬度預設填滿父元素 | 有作用 | 有作用 |
inline(行內) |
span、a、strong、em、code |
跟文字排在同一行,寬度由內容決定 | 沒有作用 | 不會推開上下的內容 |
inline-block |
button、input(瀏覽器的預設) |
跟文字排在同一行,但可以設尺寸 | 有作用 | 有作用 |
inline 元素是「文字的一部分」,它的盒子可能被拆到好幾行,所以 width、height 與上下 margin 都沒有作用; 上下的 padding、border 會畫出來,但不會推開上下行,容易和上一行的文字重疊。左右方向的 padding、margin、border 則正常推開旁邊的文字。
需要「跟文字同一行,又要能設定大小」的時候(標籤、徽章、按鈕樣式的連結),改成 inline-block:
.tag {
display: inline-block; /* 仍然和文字排在同一行 */
width: 80px; /* 現在有作用了 */
margin-top: 12px; /* 上下外距也會推開內容 */
}
反過來,選單的連結預設是 inline,只有文字的範圍可以點。改成 display: block 之後,連結佔滿一整行,整行都可以點,手機上也比較好按。
display: none 會讓元素完全不顯示、也不佔空間,第 16 課會和其他隱藏方式一起比較;flex、grid 是第 17~20 課的主角。
小測驗:padding、margin 與 display
對 <span class="badge"> 設定 width: 100px; height: 40px;,畫面上沒有任何變化。原因是?
padding: 10px 20px; 代表什麼?
卡片有淺藍色背景。想讓文字離卡片邊緣遠一點,而且多出來的空白也是淺藍色,要調整哪一個?
重點整理
- 每個元素都是一個盒子,由內而外是 content、padding、border、margin;padding 有背景,margin 永遠透明。
- padding、margin 四個值的順序是上、右、下、左;兩個值是上下、左右。
- 預設的
content-box中 width 只算內容,padding 與 border 另外加;border-box的 width 包含 padding 與 border。 常見做法是在全站寫*, *::before, *::after { box-sizing: border-box; }。 - 一般區塊排列中,上下相鄰的外距會合併成較大的那一個;Flex、Grid 項目不會合併。
- 有寬度的區塊用
margin: 0 auto水平置中。 block自己佔一行;inline跟文字同一行,width、height 與上下 margin 沒有作用;inline-block同一行又能設大小。
下一課講背景、邊框與陰影:背景圖片與漸層、圓角、陰影,以及透明度。