[基礎課程] CSS 教學(三):盒子模型與定位點
若順著教材讀到這裡,你會發現很多 CSS 的控制成敗都跟 block 或 inline 有關。這裡我們會完整介紹什麼是盒子模型 (Block & Inline),有了完整的盒子概念後,就能接著會介紹如何去定位這些元素到指定的位置,學完這篇能幫助你整個 CSS 切版功力大幅提升。
盒子模型 Box Model
- CSS 是盒子的世界,分為兩種基本盒子分為區塊盒(block box)與行內盒(inline box)。
- 在編寫 CSS 時先確認 HTML 標籤的原型是區塊標籤還是行內標籤,這寫 CSS 時非常重要的事。
- HTML 的每個標籤都可以視為盒子,當使用了標籤等於建立盒子產生容器區塊,也稱為內容區域。
- 外距、內距的單位百分比 %,是根據父元素的單位做為計算,也就是單位 % 不論是上下左右的內外距都是依外層寬高值而影響。
- img 圖片雖為行內元素,但表現像 inline-block,非常適合解釋 background-color 包含 padding。
- 有些標籤會有瀏覽器預設的上下外距,例如 h、p、body、ul。
以 div 區塊標籤、span 行內標籤為經典代表。
<div class="block_box">Lorem ipsum dolor sit amet.</div> |
區塊盒(block box)
常見的區塊型標籤:div、h1、p、ul、li…
- block 可用 margin 與 padding 屬性,標籤前後都有結束斷行處理,且會填滿撐大父元素,預設自動占滿整體寬度。
- 會將高度延展到可容納它本身所存放的所有東西,也就是利用內容去撐開它的高度。
- block 並不會受到 vertical-align 影響。
區塊盒的內容區域(width)、內距(padding)、邊框(border)、外距(margin)屬性都是有效可指定的。
行內盒(inline box)
常見的行內型標籤:span、a、em…
- 循規蹈矩且前後不會結束斷行,內容多少寬就多少,即是設定 width、height 都不會有作用。
- 上下的 margin 無效,但左右的 margin 依舊有效。
- padding、border 上下左右皆有效。
- height 與 width 屬性無效,原因在於行內無法設定寬、高。
- 可以被 vertical-align 影響。
行內區塊盒(inline-block box)
算是兩者的合併體系,主要可以當作不會斷行 (inline) 且可以彈性指定內外距 (block)。
- 繼承 inline,預設內容多少寬度就多少,不會強迫換行。
- 繼承 block,可使用 margin, padding, width, hegiht, border 都可指定。
舉例示範
使用標籤產生盒子,即產生容器區塊,目前內容區域沒有東西,瀏覽畫面還看不到東西
<div></div> |
block 一旦有了內容後,產生了有內容的內容區域,內容區域會以區塊(block)或行內(inline)來顯示。
- 區塊是橫行霸道的佔位王,即便它的內容很少,也要佔據整列。
- 區塊可以自由使用寬(width)、高(height)、內距(padding)、外距(margin)
<div>Lorem ipsum dolor sit amet.</div> |
inline 行內是循規蹈矩的乖乖排,它的內容就已經是它的寬度,許多行內盒子會依序乖乖排好自己的位置。 行內不能設定寬、高,上、下外距無效。
<span>Lorem ipsum dolor sit amet.</span> |
科普知識:initial、inherit、unset
如果有機會,你會發現很多屬性基本上都額外支援這三個通用值,決定目前的元素取值來源如何判斷但實用性不高。分為 initial(預設:繼承父層)、inherit(該標籤原始樣貌)、unset(清除不指定)。
盒子模型參數
計算一個區塊實際寬度為:實際總寬度 = width + padding + border + margin。
內容區域 | 內距 | 邊框 | 外距 |
---|---|---|---|
content(width) | padding | border | margin |
- 可看到的尺寸:內容區域、內距、邊框。
- 看不到的尺寸:外距。
width、height
盒子設定的寬、高是內容的容器,如果高沒有設定則是 auto,隨著內容長高。
div { |
Margin、Padding
padding 內距沒有負值,margin 外距可以有負值。
/*margin*/ |
科普知識:外距重疊現象(margin collapsing)
若有兩個 block 容器的 margin 重疊,大的 margin 會吃掉小的 margin
,較大的外距才會有效果,較小的會被瓦解。
Border
邊框最主要的三個屬性分別是 style 樣式、width 寬度、color 顏色。
border-style | 說明 |
---|---|
dotted | 方格點 |
dashed | 短線 |
double | 雙實線 (border-width 屬性值是兩條線的總和) |
groove | 看起來像刻入頁面 |
ridge | 看起來像凸出頁面 |
inset | 看起來像嵌入頁面 |
outset | 看起來像浮雕 |
dotted 會依照 border-width 改變方格的大小及間距,如果邊框是 10px,那麼方格則是 10px 且每個點之間有 10px 的間距。
Border 樣式:
/********************************** border-style */ |
Border 顏色:
/********************************** border-color*/ |
Border 寬度:
/********************************** border-width */ |
border 簡寫屬性
- Border 簡寫屬性只針對 width | style | color 之組合三項應用,複選最需少一項(其他項則自動為預設),最多三個。
- 一但使用 Border 縮寫方式後,原本的所有 border-style,border-width,border-color 都會失效。則縮寫為主要優先且不互通。
- border 簡寫也有全部或局部,可分開指定。
/********************************** border */ |
border-radius 圓角
border-radius 其實也是個簡寫屬性,其實是
border-top-left-radius
、border-top-right-radius
、border-bottom-right-radius
、border-bottom-left-radius
所簡化並對應四邊角。除了原本功能的局部(上述所寫),縮寫屬性時可支援全部、四邊設定 (↖↘ ↙↗ 或 ↖ ↗ ↘ ↙)。
簡寫時所塞入單位為該角上的遮罩圓之半徑,一個單位上的半徑有分水平向跟垂直向。
硬要說的話其實有 8 個圓角錨點(雙半徑),完整寫法為
border-radius: 10px 20px 30px 40px / 50px 60px 70px 80px
各別代表水平組↖ ↗ ↘ ↙ / 垂直組↖ ↗ ↘ ↙;當部分錨點未指定時,會自動尋找簡寫屬性的該單位之合併規則描述。
如果很難懂,可以透過這個網站工具清楚說明錨點的設定變化。
示範如下:
/********************************** border-width */
div{
border-radius: 10px; /*all*/
border-radius: 25% 10%; /* ↖↘ ↙↗ */
border-radius: 10px 20px 30px 40px; /* ↖ ↗ ↘ ↙ */
border-radius: 1em/5em;
/* 等同於: */
border-top-left-radius: 1em 5em;
border-top-right-radius: 1em 5em;
border-bottom-right-radius: 1em 5em;
border-bottom-left-radius: 1em 5em;
border-radius: 4px 3px 6px / 2px 4px;
/* 等同於: */
border-top-left-radius: 4px 2px;
border-top-right-radius: 3px 4px;
border-bottom-right-radius: 6px 2px;
border-bottom-left-radius: 3px 4px;
}當 border-collapse: collapse 成立時(共享 border),圓角化將無效。
就算 border 不存在,圓角效果也能影響 background。
範例步驟:
div { |
跟著做:用 border-radius 劃一個圓型
p { |
outline 外側
是外側的 border 效果,雖然視覺上算占用 margin 上,但實際上他屬於 border 項目。
div { |
小節練習:算出以下 margin、width 之 auto 值
素材準備:準備代碼以方便下階段的教學練習
<style> |
試著根據不同的追加屬性之描述,對 p 的盒子現象進行了解
求 p 的 margin-left 的 auto 值。
p {
width: 100px;
margin-left: auto;
margin-right: 100px;
}ANS: p 的 margin-left:auto 經計算後為 300px
分析 margin 狀況與實際值
p {
width: 100px;
margin-left: 100px;
margin-right: 100px;
}ANS: 此情況會產生過度受限 (Overconstrained),結果 margin-right 會被迫成為 auto(算出 300px)
求 width 的 auto 值
p {
/* width 不寫時也會預設為 auto */
/* width: auto; */
margin-left: 100px;
margin-right: 100px;
}ANS: width 計算後為 300px
求 margin 左右的實際值
p {
width: 300px;
margin-left: auto;
margin-right: auto;
}ANS: margin 左右會平均的分配到 100px,也就是常見的至中效果。
求 width 跟 marget 的狀況與實際值
p {
width: auto;
margin-left: auto;
margin-right: 100px;
}ANS: 會以 width 為準算出 400px,margin-left 為 0
求 width 跟 marget 的狀況與實際值
p {
width: auto;
margin-left: auto;
margin-right: auto;
}ANS: 會以 width 為準算出 500px,margin 左右皆為 0
常見的屬性組合
box 模型常常會搭配以下的屬性做組合上的變化。
display
調整元素的顯示方式,通常拿來切換 block 與 inline,隱藏用途,或是直接改用 flexbox 或 table 的調整。
display | 說明 |
---|---|
block | 區塊 |
inline | 行內 |
inline-block | 行內區塊 |
flex | 彈性盒 |
inline-flex | 行內彈性盒 |
table | 父容器 |
table-cell | 子容器變 td |
table-row | 子容器變 tr |
none | 消失 |
min-width & max-width
最小寬度與最大寬度的限定屬性,好處於能安全的混合搭配其他長度單位,使得瀏覽器在不同的 viewpoint 上有不同的長度單位可規範。
- min-width:當瀏覽器過窄時 box 開始擠壓。當 width 達到指定 min-width 時將固定此寬度為優先,並停止擠壓內容。
- max-width:當瀏覽器過大時 box 開始延展。當 width 達到指定 max-width 時將固定此寬度為優先,並停止延展內容。
也有 min-height & max-height,但如果 max-height 設定內容超過該高度時,內容會溢出 overflow。
舉例 1: 最大寬度為 500px,若小於 500px 寬度會以 40% 設定
<style> |
舉例 2: 先控制最大寬度!不管介面有多大都不會超出這個範圍。當瀏覽器小於指定的 max-width 1200px 寬度時,會用原先設定的寬度百分比 80% 來計算。
.container { |
Box-sizing
重新定義 Box 寬度計算方式。
Box-sizing | 說明 |
---|---|
content-box | width = content area,預設 |
border-box | width = content + padding + border |
在 border-box 模式下,border、padding 都會算在寬度裡面,只需考量處理 margin。bootstrap 便是採用 border-box 模式。
*, *::after, *::before { |
有些設計師建議將所有元素都設定為 border-box,更容易計算盒子的寬度。
overflow
如果對區塊設定高度 Height,內容如果超過高度則會產生內容溢出。所以設定頁面元素高度的鐵則是不要設定,若超出時可以設定 overflow 如何處理溢出。
p.one { |
更細一點的參數如 overflow-x 與 overflow-y,能對 x 與 y 軸進行溢出處理。
舉例:使用 max-height 來建立純 CSS 的捲動軸
<style> |
visibility
visibility 屬性可以讓盒子隱藏不見,但將原有的位置保留下來,反而 display:none 並不會保留空間下來。
.a { |
定位 Position
最重要的定位方式為
絕對定位
與相對定位
。定位時都需要咬著一個容器(空間座標)才能讓元素之後能進行偏移座標。按道理來說容器(座標)應該是指父容器,但這不是絕對的。容器區塊 (containing block) 就是包含元素的區塊,譬如
<html><body></body></html>
這現象 html 就是屬於 body 的父容器,同理 body 也將會是所有網頁元素的大容器。元素生成時會從父容器那取得初始顯示位置,如果:
- 該元素設定為
相對定位
=> 初始顯示位置不變、保留容器給的Containing 位置
、以Containing 位置
給的容器範圍為空間座標,以自己的空間座標做相對偏移。 - 該元素設定為
絕對定位
=> 初始顯示位置不變、捨棄容器給的Containing 位置
,向祖先尋找最近已存在 Position 的空間座標做基準(最後找不到則認 viewpoint),以別人的空間座標做絕對偏移。 - 該元素設定為
固定定位
=> 初始顯示位置不變、捨棄容器給的Containing 位置
,直接向瀏覽器的 viewpoint 做基準,以網頁的空間座標做絕對偏移。
- 該元素設定為
每產生一個具備偏移座標的 Position 時,就等於一個圖層產生,就能使用 z-index 屬性做圖層堆疊控制。
相對定位 relative
設定 position:relative
,區塊會以原本顯示的位置為基準可以指定上下左右偏移。 與 absolute 不同的是,區塊原本的空間仍會保留。若只設定了 position:relative,卻沒有指定偏移則顯示上不會有什麼明顯有什麼改變。
絕對定位 absolute
當子元素設定 position:absolute
,區塊會暫時以目前位置顯示,並脫離原本的區塊空間並向上尋找持有定位(可以是 relative 或是 absolute) 的容器做偏移基準,也就是版面基準點來定位,畫面上因為脫離了所以原本容器內的空間將會釋放掉。
- 若定位的容器是 block,
Containing 範圍
會設為該容器的 padding 之邊緣。 - 若定位的容器是 inline,
Containing 範圍
會設為該容器的 content 之邊緣。 - 若定位的容器不存在,
Containing 範圍
定義為網頁的整體容器區塊。 - 設定 position 才可以使用 z-index 屬性。(重要)
固定定位 position:fixed
absolute 與 fixed 的行為很像,不一樣的地方在於 absolute 元素的定位是會去找有安排定位之容器,真的找不到才會採網頁的定位之容器。fixed 是直接任網頁的定位之容器當偏移基準。
範例說明 relative & absolute & fixed
這裡直接實作比較三者差異會更容易理解。準備一個 5*4 的方塊圖,試著讓資料在畫面中央。
素材準備:準備代碼以方便下階段的教學練習
<body> |
body { |
小技巧:inline 的 4px 間格
此時你應該發現畫面的排版不如預期正確,因為需要修正 inline 所影響的 4px 間格。這裡提供兩種方法解決:(方法很多可以上網科普)
方法一:消除看不見的文字
/* |
方法二:捨棄 inline 改用 block+float
/* |
後續教學上我們採用方法一來解決 (較多狀況可以解析,如果可以兩法都去理解)。此時你的畫面應該如下:
添加三種 position 效果到各位置。理解這些定位點解析:
style.css .relative {
position: relative;
background: brown;
top: -30px;
left: -30px;
/* 空間仍在僅偏移 */
}
.absolute {
position: absolute;
background: blueviolet;
/* 預設位置不變,父容器失去該元素 */
}
.fixed{
position: fixed;
background: darkgreen;
/* 解讀奇怪的預設位置 =>
1. 因為 inline-block 關係,會緊鄰前者旁邊
2. 失去父容器的強迫換行,所以 overflow
*/
}試著解讀以下各種狀況:
- .absolute 設定偏移量 (top,bottom,left,right) 尋找落點
- .bigbox, .main, body 選一組設定 relative,尋找。absolute 落點
- .fixed 設定偏移量 (top,bottom,left,right) 尋找落點
- 3 組都設定 z-index 屬性,帶入正值與負值解析圖層位置
- 挑幾組號碼給他指定 absolute 或 relative 看看圖層位置
預覽範例效果
利用 absolute 特性 拿來做水平垂直至中。
素材中是使用 body 指定 flex 方式設計。另一個方式為:
.main{ |
新手陷阱:當子元素 fixed 遇到 父容器 Transform
雖然表面上說 fixed 直接參考 viewpoint,但根據上面至中效果你會發現一個問題,那就是 案例中的小方塊 fixed 在向上尋找 viewpoint 時,途中遇到到 .main
父容器這裡的 transform,會導致放棄尋找 viewpoint 而定位至此。
如果要排除此現象,你得想辦法不要出現 transform 的 position 至中寫法:
.main{ |
黏性定位 position:sticky
這是一個結合了 position:relative (進入)和 position:fixed (離開)的特性,適用像是 header-nav 選單效果,會先以 relative 效果方式正常出現,當元素離開 viewport 時因為溢出成立導致會轉成 fixed 效果。
以下部分情況下使用 sticky 會失敗(就是 fixed 條件失敗):
- 未指定特定偏移量 (top,bottom,left,right)=> 導致離開時沒有絕對位置。
- 父元素的 overflow 屬性值為 hidden, auto, scroll=> 無法溢出導致不成立。
- body height:100%=>元素永遠離不開 viewpoint。
小節練習 - Postition
請使用 CSS 的 postion 屬性試著做出以下畫面,點符號利用 absolute 做出。每個骰子的尺寸為 200px,點符號尺寸為骰子的 0.2 倍大。
<div class="container"> |
.dice { |
總思考練習
卡片排版
思考以下問題:
- 單看預覽,hot 定位的方式透過什麼方式完成
- 為何檢查
<div class="container">
的參數只有寬度沒有高度 - 在 CSS 代碼中為何沒有
box-sizing: border-box;
時,整個卡片會跑版。
解答
- 父容器 relation 與子元素 absolute 與 top&right
- 父容器有綁定 width 但子元素 float
- 父容器總寬 960,瀏覽器預設是 content-box(width = content) 模式所以全部算入一共是 m20+b2+p20+w300=>342*3>960 超過,反之 border-box 模式 (width = content + padding + border) 下為 m20+w300=320*3=960。
廣告鎖頻
本練習包含 JQ 語法負責按鈕控制(未學習過不影響本題思考),思考以下問題:
- 為何預覽上 div.content 無法被選取文字,其原理為何。
- 試著理解本題 AD 的水平垂直至中的方法。
解答
.ad
最上面設定 z-index,下一層的.all
,最後一層是 body 其他的元素。- 利用 fixed 變成圖層後,box 範圍利用 left,right,top,bottom 故意 0 撐大邊界使得 margin:auto 幫忙置中,前提是 content 必須要有固定的 w 跟 h 才能讓 auto 計算得出來。