CSS學習筆記(一) 盒子模型

北冥有隻魚 發表於 2022-11-27
CSS
本來按照正常的學習順序, 應該是HTML、CSS、JavaScript,再到Vue。但是到我這裡順序就反過來了一樣,先Vue 、再JavaScript,然後CSS。原因在於我之前就學習過這些,雖然當初學的不紮實,但是勉強能用,需要用到什麼,然後忘記了就重新再學習一下,這看起來像是逆生長一樣,但後面會將這個系列的文章補齊。

前言

在上海地鐵是大多數人的通勤方式,有的時候地鐵人多,就會很擁擠,那擁擠表達的意思是什麼呢? 我想是人與人之間的距離很小吧。如果將人視做html的元素,那麼人與人間的距離在在CSS中稱為margin,也被稱為外邊距。但是人和HTML的標籤還是有些區別,HTML的元素分塊級元素和行內元素、

塊級元素(Block-level elements)

那什麼是塊級元素? 簡單的說就是塊級元素佔滿一行,如下圖所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<p>This</p>
</body>
<style>
p { 
        background-color: #8ABB55;
 }
</style>
</html>

效果圖:

塊級元素

雖然標籤p的內容只有一個this,但是當我們為它新增上背景色,你會發現整個一行都出現了綠色。細心的朋友執行程式碼之後可能會說也沒佔滿一行啊,旁邊還有兩個空白啊。那是因為我們寫的p標籤在body標籤裡面,body標籤是一個更大的盒子:

body標籤

瀏覽器給body預設的margin大小是8個畫素, 我們可以透過瀏覽器的工具來驗證我們的論斷:

p標籤

body標籤佔8px

然後我們將body標籤的margin清空就會發現p標籤完全佔一整行:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<p>This</p>
</body>
<style>
p { 
        background-color: #8ABB55;
        margin: 0px;
 }
body{
    margin: 0px;
} 
</style>
</html>

所以塊級元素佔據其父元素的整個水平空間,高度等於其內容高度,像是一塊一樣,所以被稱為塊級元素。所以兩個塊級元素不能位於一行,預設情況下在塊級元素之後,再寫塊級元素,塊級元素會新起一行。以下是HTML中所有的塊級元素列表:

  • address article aside blockquote dd div
  • dl fieldset figcaption figure footer h1-h6 form
  • header hgroup hr ol p pre section table ul

行內元素

那我們可以類比塊級元素對行內元素下定義,塊級元素佔據一行,那行內元素就是隻佔據它對應標籤的邊框所包含的空間:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <p>This <span>span</span> is an inline element; its background has been colored to display both the beginning and end of the inline element's influence</p>
</body>
<style>
    span { background-color: #8ABB55; }
</style>
</html>

行內元素效果

一般情況下,行內元素只能包含資料和其他行內元素。而塊級元素可以包含行內元素和其他塊級元素。預設情況下,行內元素不會以新行開始,而塊級元素會新起一行。下面的元素都是行內元素:

  • b, big,i,samll,tt。
  • abbr,acronym,cite,code,dfn,em,kbd,strong,samp,var。
  • a,bdp,br,img,object,q,script,span,sub,sup
  • button,input,label,select,textarea。

盒子模型

但內容之間通常有距離還往往不夠用,就像一幅畫一樣,我們為了保護這幅畫通常會買一個畫框,通常畫框會比要裝的畫大一些,起到留白的效果:

盒子模型

當然也有padding(padding為0)的,瀏覽器的渲染引擎會根據標準之一的CSS基礎盒模型(CSS basic box model),將所有的元素表示為一個個矩形的盒子(box)。根據網頁中宣告的CSS,來決定這些盒子的大小、位置、以及屬性(例如顏色、背景、邊框尺寸)。每個盒子都由四個部分來組成,如上面的圖片所示,每個盒子有四個組成區域

  • 內容區域 content area:容納著元素的“真實內容”,例如文字、影像。它的尺寸為內容寬度和內容高度。通常含有一個背景顏色或背景影像。
如果box-sizing 這個屬性為預設屬性即content-box,則內容區域可以透過明確的width、min-width、max-width、height、min-height,和max-height控制。
  • 內邊距區域 padding area: 擴充套件內容區域,間距的大小可以透過明確的padding-top、padding-right、padding-bottom、padding-left和簡寫屬性padding來控制。
  • 邊框區域 border area: 擴充套件內邊距區域,是容納邊框的區域,邊框的粗細由border-width和簡寫的border屬性控制,如果boder-sizing屬性被設定為border-box。那border區域的大小可以明確透過width、min-width, max-width、height、min-height,和 max-height 屬性控制。假如框盒上設有(background-color 或 background-image),背景將會一直延伸至邊框的外沿(預設為在邊框下層延伸,邊框會蓋在背景上)。此預設表現可透過 CSS 屬性 background-clip 來改變。
  • 外邊距區域 margin area: 即盒子與盒子的距離,外邊距的大小由 margin-top、margin-right、margin-bottom、margin-left,和簡寫屬性 margin 控制。在發生外邊距合併的情況下,由於盒之間共享外邊距,外邊距不容易弄清楚。

盒子模型-檢查

上面我們提到的就屬於盒子模型,一般我們稱之為標準盒模型。在標準盒模型中,如果你給盒子本身設定width和height,實際上設定的是content box,即內容的長寬高。padding和border再加上設定的寬高一起決定盒子的大小。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<p>This</p>
</body>
<style>
p { 
        background-color: #8ABB55;
        margin: 10px;
        width: 200px;
        padding: 10px;
        border-width:10px;
        border-style: solid;
        border-color: blue;
 }
body{
    margin: 0px;
} 
</style>
</html>

實際效果:

標準盒子模型示例

IE盒子模型

某些時候標準盒子模型也讓我們產生困擾,難道我們我們不能直接設定整個盒子的寬高嗎? 還要加上border和padding,這很麻煩。IE模型中,內容寬度是設定的寬度減去邊框和填充部分。一般情況下瀏覽器採用的都是標準模型,如果需要使用IE模型,可以透過設定box-sizing: border-box來實現。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<p>This</p>
</body>
<style>
p {     
        box-sizing: border-box;
        background-color: #8ABB55;
        margin: 10px;
        width: 200px;
        padding: 10px;
        border-width:10px;
        border-style: solid;
        border-color: blue;
 }
body{
    margin: 0px;
} 
</style>
</html>

IE盒子模型

如果你希望所有元素都使用IE盒子模型,那我們可以將box-sizing在html元素上,然後設定所有元素繼承該屬性,如下面程式碼所示:

html {
  box-sizing: border-box;
}
*, *::before, *::after {
  box-sizing: inherit;
}

IE瀏覽器預設使用IE盒子模型,不支援切換標準盒子模型(IE8+ 支援使用box-sizing進行切換)

塊級盒子和內聯盒子

HTML中元素分為塊級元素和行級元素,在CSS中盒子也分為塊級盒子和內聯盒子。塊級盒子由塊級元素產生,內聯盒子由行內元素產生。塊級元素會表現出以下行為:

  • 盒子會在內聯方向擴充套件並佔據父容器在該方向上所有可用空間,在絕大多數情況下意味著盒子會和父容器一樣寬。
  • 塊級盒子不能並列,每個盒子獨佔一行。
  • width和height屬性可以發揮作用
  • 內邊距(padding), 外邊距(margin)和邊框(border)會將其他元素從當前盒子周圍推開。

除非特殊指定,注入標題h1等和段落在預設情況下都是塊級的盒子。我們可以透過display屬性來將當前元素設定為塊級元素或內聯元素。display常見的屬性有:

  • block: 將當前盒子設定為塊級盒子
  • inline: 將當前盒子設定為內聯盒子
  • inline-block: 我們有的時候會希望元素具備寬度和高度屬性,但是有具備同行特性。在這種情況下就要可以使用inline-box。

如果一個盒子為內聯盒子,那麼這個盒子具備以下性質:

  • 盒子不會換行
  • width和height屬性不起作用
  • 垂直方向的內邊距(padding)、外邊距(margin)以及邊框(border)會被應用但是不會把其他處於 inline 狀態的盒子推開。
  • 水平方向的內邊距、外邊距以及邊框會被應用且會把其他處於 inline 狀態的盒子推開。

用做連結的 <a> 元素、 <span><em> 以及 <strong> 都是預設處於 inline 狀態的。

我們可以透過display屬性的設定,比如inline或者block,來控制盒子的外部顯示型別。那麼有外部顯示型別,就會有內部顯示型別。外部顯示型別決定盒子是塊級還是內聯,而內部顯示型別則決定盒子內部元素是如何佈局的。預設情況下和其他塊元素以及內聯元素一樣。但是我們也可以透過使用flex的display屬性值來更改內部顯示型別。如果設定display:flex,在一個元素上,外部顯示類型別是block,但是內部顯示型別修改為flex。該盒子的所有直接子元素都會變成flex元素,會根據彈性盒子flexbox 規則進行佈局,我們會在後文介紹這些內容。

塊級和內聯佈局是web上的預設行為,有的時候,他們也被稱為正常文件流。

外邊距

外邊距是盒子周圍一圈看不到的空間。它會把其他元素從盒子旁邊推開。外邊距屬性值可以為正,也可以為負。設定負值會導致和其他內容重疊。無論使用標準模型還是替代模型,外邊距總是在計算可見部分後額外新增。我們可以使用margin屬性一次控制一個元素的所有邊距,或者每邊單獨使用等價的普通屬性控制:

  • margin-top
  • margin-right
  • margin-bottom
  • margin-left
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="container">
        <div class="box">Change my margin.</div>
      </div>
          
</body>
<style>
.box {
  margin-top: -40px;
  margin-right: 30px;
  margin-bottom: 40px;
  margin-left: 4em;
  border: 5px solid rebeccapurple;
  background-color: lightgray;
  padding: 10px;
   height: 150px;
}
.container {
    border: 5px solid blue;
    margin: 40px;
}
</style>
</html>

效果圖

嘗試更改外邊距的值,感受一下在外邊距設定為正時是如何推開周邊元素,以及設定為負時是如歌重疊的。

外邊距摺疊

理解外邊距的一個關鍵是外邊距摺疊概念,如歌你有兩個外邊距相接的元素,這些外邊距將合併為一個外邊距,即最大的單個外邊距的大小。在下面的例子中,我們有個兩個p標籤,第一個標籤的margin-bottom為50px。第二段的margin-top為30px。因為外邊距摺疊的概念,所以框之間的實際外邊距是50px,而不是兩個外邊距的總和。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="container">
        <p class="one">I am paragraph one.</p>
        <p class="two">I am paragraph two.</p>
      </div>
</body>
<style>
.container {
    border: 5px solid blue;
    margin: 100px;
}
p {
    border: 5px solid rebeccapurple;
    background-color: lightgray;
    padding: 10px;
}  
.one {
  margin-bottom: 50px;
}

.two {
  margin-top: 30px;
}
</style>
</html>

可以嘗試修改一下.two的margin-top,只要小於50px,兩者的距離不會發生變化,當大於五十的時候按.two的摺疊。

外邊距摺疊

內邊距

內邊距位於邊框和內容區域之間。與外邊距不同,不可以設定負值。應用於元素的任何背景色都將顯示在內邊距中。我們可以使用padding簡寫屬性控制元素所有邊,或者每邊單獨使用等價的普通屬性:

  • padding-top
  • padding-right
  • padding-bottom
  • padding-left
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="container">
        <div class="box"> 改內邊距</div>
    </div>
</body>
<style>
.container {
    border: 5px solid blue;
    margin: 40px;
    padding: 20px;
}
.box {
    padding-top: 40px;
    padding-right: 20px;
    padding-bottom: 40px;
    padding-left: 4em;
    border: 5px solid rebeccapurple;
    background-color: lightgray;
}
</style>
</html>

內邊距

邊框

相對於內邊距和外邊距,邊框的屬性很多,可以設定邊框的寬度、邊框顏色,邊框樣式(邊框是虛線 還是直線)。上面的例子我們已經有意無意的用過了:

 border: 5px solid blue;

這是為四個邊框統一設定寬度、樣式、顏色。我們也可以分別設定四個邊框的寬度、樣式、顏色:

  • border-top
  • border-right
  • border-bottom
  • border-left

設定所有邊的寬度、樣式、顏色也可以這麼宣告:

  • border-width
  • border-style
  • border-color

我們也可以單獨設定每個邊的寬度、樣式、顏色:

  • border-top-width
  • border-top-style
  • border-top-color
  • border-right-width
  • border-right-style
  • border-right-color
  • border-bottom-width
  • border-bottom-style
  • border-bottom-color
  • border-left-width
  • border-left-style
  • border-left-color

總結一下

盒子模型類似於搬家之後對東西進行重新擺放,這也被稱為佈局,如果房子小,margin就會小就會顯得很擁擠。一個良好的佈局會讓居住者賞心悅目。本篇基本參考了《千古前端圖文教程》,《MDN web docs》。這裡用我自己的方式將這些內容整合了一下。注意JavaFX我們前面寫的文章也會有這樣的佈局問題,兩者是通用的。

參考資料