線上課程觀課進度管理小工具開發日誌
Day 6:JavaScript 程式碼執行排序:遞迴函數、Call Stack、Task Queue
Call Stack;遞迴函數與 call stack 的關係;Task Queue(非同步的概念再釐清)
Day 9:「修煉圈圈 Practice Rings」Web app 開發日誌 - 2025 六角 AI Vibe Coding 體驗營期末大魔王作業
這份筆記旨在記錄 2025 六角 Vibe Coding 體驗營每日作業 Day 21 的期末專案成果,從一個簡單的個人痛點出發,透過與 AI(在我這個情境中是 Perplexity)協作,在一天內完成了一個包含前後端的全端網頁小工具:「修煉圈圈 Practice Rings」。
記錄雞蛋糕的每一步前端煉成過程,從小白到(也許是)前端工程師的學習與分享。

Subscribe to 雞蛋糕的前端修煉屋
線上課程觀課進度管理小工具開發日誌
Day 6:JavaScript 程式碼執行排序:遞迴函數、Call Stack、Task Queue
Call Stack;遞迴函數與 call stack 的關係;Task Queue(非同步的概念再釐清)
Day 9:「修煉圈圈 Practice Rings」Web app 開發日誌 - 2025 六角 AI Vibe Coding 體驗營期末大魔王作業
這份筆記旨在記錄 2025 六角 Vibe Coding 體驗營每日作業 Day 21 的期末專案成果,從一個簡單的個人痛點出發,透過與 AI(在我這個情境中是 Perplexity)協作,在一天內完成了一個包含前後端的全端網頁小工具:「修煉圈圈 Practice Rings」。
<100 subscribers
<100 subscribers
CSS 選擇器語法與層疊優先級
Normal Flow 排版機制
Block vs Inline 元素特性
Position 定位與 z-index 堆疊
Margin Collapsing 規則
HTML/CSS vs JavaScript 除錯差異
問題情境:為什麼 <a class="a b">HELLO</a> 配上以下 CSS 會顯示紅色而非藍色?
a { color: red; }
b { color: blue; }
答案揭曉:因為 b 是元素選擇器,選中的是 <b> 標籤(如 <b>粗體</b>),而非 class="b"! developer.mozilla
直接使用 HTML 標籤名稱,不需要任何前綴符號: udn.realityripple
/* 選取所有 <a> 標籤 */
a {
color: red;
}
/* 選取所有 <p> 標籤 */
p {
font-size: 16px;
}
在 class 名稱前必須加上點(.)符號: developer.mozilla
/* 選取所有 class="menu" 的元素 */
.menu {
background: blue;
}
/* 選取所有 class="btn-primary" 的元素 */
.btn-primary {
padding: 10px;
}
選擇器類型 | 語法 | 範例 | 選取目標 |
|---|---|---|---|
元素選擇器 |
|
| HTML 標籤本身 |
類別選擇器 |
|
| 含有該 class 的元素 |
在 HTML 的 class 屬性中,空格用來分隔多個類別名稱: udn.realityripple
<!-- 這個 div 同時擁有兩個類別:primary 和 button -->
<div class="primary button"></div>
<!-- 這個 a 同時擁有三個類別:nav, link, active -->
<a class="nav link active">Home</a>
單一類別名稱內部不能包含空格,需要多個單字時使用連字符號:
<!-- ✓ 正確:使用 kebab-case -->
<button class="btn-primary"></button>
<!-- ✗ 錯誤:會被解讀成兩個類別 btn 和 primary -->
<button class="btn primary"></button>
/* 選中所有 class="primary" 的元素 */
.primary { color: red; }
/* 選中所有 class="button" 的元素 */
.button { padding: 10px; }
/* 選中「同時擁有 primary 和 button」的元素(中間無空格) */
.primary.button { border: 1px solid; }
/* 選中「primary 裡面的 button」(中間有空格) */
.primary .button { margin: 5px; }
CSS 像油漆工在牆上一層層塗油漆,後塗的會覆蓋先塗的。 developer.mozilla
當多條 CSS 規則衝突時,瀏覽器按照這個順序判斷: developer.mozilla
優先級(Specificity):選擇器越精準,優先權越高
來源順序(Source Order):優先級相同時,後面的覆蓋前面的
重要性(!important):強制覆蓋所有規則(不建議濫用)
選擇器類型 | 優先級值 | 範例 | 說明 |
|---|---|---|---|
Inline style | 1-0-0-0 |
| 最高 |
ID | 0-1-0-0 |
| 高 |
Class / 偽類 / 屬性 | 0-0-1-0 |
| 中 |
Element / 偽元素 | 0-0-0-1 |
| 低 |
<a class="a b">HELLO</a>
.a { color: red; } /* 優先級:0-0-1-0 */
.b { color: blue; } /* 優先級:0-0-1-0 */
結果:藍色
原因:兩個選擇器優先級相同,.b 在後面所以覆蓋 .a developer.mozilla
<a class="a b">HELLO</a>
a.b { color: blue; } /* 優先級:0-0-1-1 (1個元素+1個類別) */
.a { color: red; } /* 優先級:0-0-1-0 (1個類別) */
結果:藍色
原因:a.b 優先級較高,不管 CSS 出現順序 developer.mozilla
題目:
<a class="a b">HELLO</a>
a{ color:red }
b{ color:blue }
拆解過程:
HTML 解讀:這是一個 <a> 標籤,同時擁有兩個類別:a 和 b
CSS 解讀:
a{ color:red } 是元素選擇器,選中所有 <a> 標籤 ✓
b{ color:blue } 是元素選擇器,選中所有 <b> 標籤(如 <b>粗體</b>) ✗
結果:只有第一條規則套用,顯示紅色
正確寫法:
a{ color:red } /* 選中 <a> 標籤 */
.b{ color:blue } /* 選中 class="b" 的元素 */
這時兩條規則都會套用,.b 優先級較高,最終顯示藍色。
/* ✗ 錯誤:這是選 <menu> 標籤,不是 class="menu" */
menu { color: red; }
/* ✓ 正確:這才是選 class="menu" */
.menu { color: red; }
b { color: blue; } /* 選 <b> 標籤 */
.b { color: blue; } /* 選 class="b" */
<!-- 這兩個完全一樣 -->
<div class="a b"></div>
<div class="b a"></div>
CSS 規則的出現順序才會影響層疊結果,HTML 中 class 的順序無關。
.a.b { } /* 同時擁有 a 和 b 類別的元素 */
.a .b { } /* a 類別元素裡面的 b 類別元素 */
**Normal Flow(正常流)**是瀏覽器排版的預設模式,所有「佔空間」的元素都在這個流程裡排隊。在 normal flow 中,元素只有兩種排列方式:block(區塊級) 或 inline(行內級)。 docs3.w3cub
Block 元素會「獨佔一整行」,一個接一個垂直往下堆疊。 udn.realityripple
<div>第一個區塊</div>
<p>第二個區塊</p>
<div>第三個區塊</div>
視覺結果:
每個 block 元素自動換行,就算內容很短也會佔滿父元素的寬度。 udn.realityripple
特性 | 說明 |
|---|---|
寬度 | 預設佔滿父元素 100% 寬度 |
高度 | 由內容撐起,可設定 |
可設定尺寸 |
|
可設定留白 |
|
換行 | 前後都會自動換行 |
<div> - 通用容器
<p> - 段落
<h1> ~ <h6> - 標題
<ul> <ol> <li> - 列表
<section> <article> <header> <footer> - 語意化容器
Inline 元素會「在同一行內橫向排列」,像文字一樣左右相鄰,遇到容器邊界才換行。 udn.realityripple
<span>第一段</span>
<strong>第二段</strong>
<a href="#">連結</a>
視覺結果:
所有 inline 元素會擠在同一行,除非容器寬度不夠才會自動換行。 udn.realityripple
特性 | 說明 |
|---|---|
寬度 | 只佔內容所需的寬度 |
高度 | 由內容撐起,無法設定 |
可設定尺寸 |
|
可設定留白 |
|
換行 | 不會自動換行,和其他 inline 元素排在同一行 |
<span> - 通用行內容器
<a> - 超連結
<strong> <em> - 文字強調
<img> - 圖片(特殊的 inline 元素)
<input> <button> - 表單元素
垂直方向一個接一個堆疊
上下 margin 會發生「margin collapsing(合併)」
寬度預設是父元素的 100%
水平方向左右並排
遵循文字排版規則(對齊基線、響應 text-align)
寬度由內容決定,無法手動設定
<!-- ✅ 正確:Block 包 Inline -->
<div>
這是段落,裡面有 <span>行內元素</span>。
</div>
<!-- ❌ 錯誤:Inline 包 Block -->
<span>
<div>區塊元素</div> <!-- 語意錯誤,瀏覽器可能修正 -->
</span>
雖然 HTML 元素有預設的 block/inline 屬性,但可以用 CSS 的 display 改變: udn.realityripple
/* 把 inline 元素改成 block */
span {
display: block; /* 現在 span 會獨佔一行 */
width: 200px; /* 可以設定寬度了 */
}
/* 把 block 元素改成 inline */
div {
display: inline; /* 現在 div 會並排 */
width: 200px; /* 無效,因為 inline 不能設定寬度 */
}
值 | 行為 | 適用情境 |
|---|---|---|
| 獨佔一行,可設定寬高 | 容器、區塊佈局 |
| 並排同行,不可設定寬高 | 文字樣式、小標籤 |
| 並排同行,但可設定寬高 | 按鈕、卡片並排 |
| 完全隱藏,不佔空間 | 條件顯示 |
display: inline-block 是特殊的混合模式: udn.realityripple
.button {
display: inline-block;
width: 100px; /* 可以設定寬高(像 block) */
height: 40px;
padding: 10px; /* 上下 padding 有效 */
margin: 10px; /* 上下 margin 有效 */
/* 但會和其他元素並排(像 inline) */
}
特性:
對外:像 inline 一樣橫向排列,不會換行
對內:像 block 一樣可以設定寬高和四方向留白
常見用途:導覽列按鈕、圖標並排、響應式卡片佈局。
CSS 的「佔空間 / 不佔空間」,其實是在講元素有沒有參與正常排版流程(normal flow)。 udn.realityripple
在 normal flow 裡,元素會跟其他元素「排隊、互相推擠」。 udn.realityripple
一個 block 元素(例如 <div>) 會一個接一個往下堆疊
調整它的 margin、padding、height 時,其他元素會跟著被推開或被擠下去
這種情況,我們就說:這個元素有佔版面空間,是 in flow 的元素
當元素被設成 position: absolute 或 position: fixed 時,會被移出 normal flow。 udn.realityripple
瀏覽器排版其他元素時,完全當它不存在
它原本在 normal flow 要站的位置,會被後面的元素填補上來
它的位置改由 top / right / bottom / left(配合最近的定位父元素或視窗)決定
當多張便利貼重疊時,z-index 決定誰在上面、誰在下面。
.note-1 {
position: absolute;
z-index: 1; /* 比較低,會在下層 */
}
.note-2 {
position: absolute;
z-index: 10; /* 數字越大越上層 */
}
數字越大,越在上面。可以用負數讓元素沉到更下層。
z-index 只對有設定 position 的元素才會作用(除了 position: static)。
/* ❌ 無效:沒有設定 position */
.box {
z-index: 999; /* 不會有任何效果 */
}
/* ✅ 有效:搭配定位屬性 */
.box {
position: relative; /* 或 absolute / fixed / sticky */
z-index: 999; /* 現在才有效 */
}
當父元素設定了 position + z-index 時,會建立一個堆疊上下文(stacking context)。
關鍵規則:子元素的 z-index 只在父元素的群組內比較,無法跨群組競爭。
只會發生在垂直的上下 margin,而且元素要在 normal flow 的 block 排版裡。 developer.mozilla
<p class="a">段落 A</p>
<p class="b">段落 B</p>
.a {
margin-bottom: 20px;
}
.b {
margin-top: 40px;
}
你可能以為 A 和 B 之間距離 = 20 + 40 = 60px,
實際上只會是 40px(較大的那個),這就叫「margin 重疊(collapsing)」。 developer.mozilla
規則:同一層、彼此相鄰、都是 block、沒有東西隔開(沒有邊框、padding、別的元素),上下 margin 就會合成一個,取最大值。 geeksforgeeks
<div class="parent">
<p class="child">文字</p>
</div>
.parent {
margin-top: 50px;
/* 沒有 padding-top、border-top、高度/內容 */
}
.child {
margin-top: 30px;
}
這時你畫面最上方看到的外距不是 50 + 30
而是 max(50, 30) = 50px,等於子元素的 margin 被「傳到」父元素外面去了
.box {
margin-top: 30px;
margin-bottom: 50px;
/* 沒有內容、沒有 padding、沒有 border、高度自動 */
}
這個 .box 的上、下 margin 會互相重疊,變成一個 50px 的外距(取較大)。 developer.mozilla
左右方向的 margin(永遠不會重疊) geeksforgeeks
display: flex 或 grid 裡的子元素 margin developer.mozilla
position: absolute / position: fixed / 浮動元素的 margin developer.mozilla
被 padding、border 或非空內容隔開時 joshwcomeau
可以用「書頁排版」來比喻 margin 的設計思路:
在排版一頁文章時,兩個段落之間通常會設計一個「段落間距」,而不是「上一段底下 10pt + 下一段上面 10pt = 合起來 20pt」,否則越多段落,間距越誇張。 joshwcomeau
CSS 設計者選擇:「如果上下兩個 margin 碰在一起,就把它視為一個段落間距」→ 所以只取最大那個,不做相加。 joshwcomeau
實體排版如果一個段落包在框裡(例如卡片),你通常希望「外框 + 內文」整體看起來的段落間距還是符合版面設計,而不是框裡的 margin 又加一層父容器 margin。 developer.mozilla
margin 重疊只發生在「垂直、block、normal flow」這個世界裡。 geeksforgeeks
如果你看到「為什麼我設很多 margin,間距卻沒想像中大」,90% 是重疊在一起了。
要「打斷」重疊,可以在中間加任何一層「墊片」:
padding
border
實際內容(哪怕是一個看不到的 ::before)
或把那個容器變成 flex/grid
HTML/CSS 和 JavaScript 屬於完全不同類型的語言,這決定了它們的錯誤處理機制和除錯方式。
特性 | HTML/CSS | JavaScript |
|---|---|---|
語言類型 | 宣告式(Declarative) | 命令式(Imperative) |
錯誤處理 | 忽略無效規則,繼續執行 | 拋出錯誤,中斷執行 |
IDE 支援 | 靠規則比對提示 | 直接從引擎獲得錯誤 |
除錯方式 | 視覺檢查 + DevTools | Console 錯誤訊息 + Debugger |
你只是「描述想要的結果」,瀏覽器會盡力實現,遇到錯誤不會中斷執行。
比喻:像「許願清單」,店員盡量滿足,但缺貨時會跳過繼續處理下一項,不會因為一個錯誤就罷工。
/* 瀏覽器遇到無效屬性會直接忽略,繼續套用其他規則 */
.box {
colur: red; /* 拼字錯誤 → 瀏覽器忽略 */
color: blue; /* 正確語法 → 套用 ✓ */
widht: 100px; /* 拼字錯誤 → 瀏覽器忽略 */
height: 100px; /* 正確語法 → 套用 ✓ */
}
CSS 規範明確規定:瀏覽器必須忽略無效的屬性/值,不會拋出任何錯誤訊息。這是設計選擇,確保即使開發者寫錯,使用者仍能看到網頁內容(容錯性 fault tolerance)。
你下達「執行步驟」,引擎必須逐行執行,遇到錯誤會立即拋出例外並中斷。
比喻:像「煮菜食譜」,每一步都要正確執行,缺了鹽就會報錯停止,不會繼續煮下去。
// 1. 語法錯誤(SyntaxError) - 執行前就會偵測
const user = nul; // ❌ SyntaxError: Unexpected token
console.log(user); // 這行不會執行
// 2. 執行時錯誤(Runtime Error)
const user = null;
console.log(user.name); // ❌ TypeError: Cannot read property 'name' of null
// 3. 錯誤捕捉機制
try {
riskyOperation();
} catch (error) {
console.error('捕捉到錯誤:', error.message);
// 程式可以繼續執行
}
用途:找出「為什麼我寫的樣式沒生效」
/* 在 Styles 面板會看到: */
.button {
color: blue; /* 被刪除線蓋掉 */
}
#submit-btn {
color: red; /* ✓ 生效,因為 ID 權重更高 */
}
操作:被刪除線劃掉的屬性就是被覆蓋的規則。
用途:解釋「為什麼這條規則贏了」
權重計算規則: developer.mozilla
(a, b, c)
a = ID 選擇器數量
b = class/屬性/偽類選擇器數量
c = 標籤選擇器數量
操作:滑鼠懸停在選擇器上,DevTools 會顯示權重計算。
用途:看到最終生效的值(經過繼承、覆蓋、預設值計算後)
使用時機:當 Styles 面板規則太多看不出問題時,直接看 Computed 找到最終值,再點擊展開追蹤來源。
用途:快速試錯,不用重新載入
實務流程:
右鍵點擊元素 → 檢查
在 Styles 面板點擊屬性值
修改數值,瀏覽器立即套用
確認效果後,回去改原始 CSS 檔案
注意:DevTools 的修改是暫時的,重新整理會消失!
Box Model 檢視器:查看元素實際的 margin、border、padding、content
Flexbox/Grid 檢視器:顯示 flex items 對齊狀況、grid cells 佈局
偽類狀態切換:強制觸發 :hover、:focus、:active 等狀態
Normal Flow(正常流):元素預設的排版系統 udn.realityripple
Block Formatting Context:block 元素垂直堆疊的規則 udn.realityripple
Inline Formatting Context:inline 元素水平並排的規則 udn.realityripple
Block = 獨佔一行的框:可設定寬高,垂直堆疊
Inline = 並排的文字流:不可設定寬高,水平排列
Inline-Block = 並排的框:兼具兩者優點
只要設定以下屬性,元素就會離開 normal flow: udn.realityripple
position: absolute 或 fixed
float: left 或 right
display: flex 或 grid(子元素會進入新的排版上下文)
筆記時間:2026-02-06
核心學習:CSS 選擇器語法、層疊順序、Normal Flow、Position、Margin Collapsing、HTML/CSS vs JavaScript 除錯差異
學習階段:Phase 2 - JavaScript 基礎 + CSS 切版
CSS 選擇器語法與層疊優先級
Normal Flow 排版機制
Block vs Inline 元素特性
Position 定位與 z-index 堆疊
Margin Collapsing 規則
HTML/CSS vs JavaScript 除錯差異
問題情境:為什麼 <a class="a b">HELLO</a> 配上以下 CSS 會顯示紅色而非藍色?
a { color: red; }
b { color: blue; }
答案揭曉:因為 b 是元素選擇器,選中的是 <b> 標籤(如 <b>粗體</b>),而非 class="b"! developer.mozilla
直接使用 HTML 標籤名稱,不需要任何前綴符號: udn.realityripple
/* 選取所有 <a> 標籤 */
a {
color: red;
}
/* 選取所有 <p> 標籤 */
p {
font-size: 16px;
}
在 class 名稱前必須加上點(.)符號: developer.mozilla
/* 選取所有 class="menu" 的元素 */
.menu {
background: blue;
}
/* 選取所有 class="btn-primary" 的元素 */
.btn-primary {
padding: 10px;
}
選擇器類型 | 語法 | 範例 | 選取目標 |
|---|---|---|---|
元素選擇器 |
|
| HTML 標籤本身 |
類別選擇器 |
|
| 含有該 class 的元素 |
在 HTML 的 class 屬性中,空格用來分隔多個類別名稱: udn.realityripple
<!-- 這個 div 同時擁有兩個類別:primary 和 button -->
<div class="primary button"></div>
<!-- 這個 a 同時擁有三個類別:nav, link, active -->
<a class="nav link active">Home</a>
單一類別名稱內部不能包含空格,需要多個單字時使用連字符號:
<!-- ✓ 正確:使用 kebab-case -->
<button class="btn-primary"></button>
<!-- ✗ 錯誤:會被解讀成兩個類別 btn 和 primary -->
<button class="btn primary"></button>
/* 選中所有 class="primary" 的元素 */
.primary { color: red; }
/* 選中所有 class="button" 的元素 */
.button { padding: 10px; }
/* 選中「同時擁有 primary 和 button」的元素(中間無空格) */
.primary.button { border: 1px solid; }
/* 選中「primary 裡面的 button」(中間有空格) */
.primary .button { margin: 5px; }
CSS 像油漆工在牆上一層層塗油漆,後塗的會覆蓋先塗的。 developer.mozilla
當多條 CSS 規則衝突時,瀏覽器按照這個順序判斷: developer.mozilla
優先級(Specificity):選擇器越精準,優先權越高
來源順序(Source Order):優先級相同時,後面的覆蓋前面的
重要性(!important):強制覆蓋所有規則(不建議濫用)
選擇器類型 | 優先級值 | 範例 | 說明 |
|---|---|---|---|
Inline style | 1-0-0-0 |
| 最高 |
ID | 0-1-0-0 |
| 高 |
Class / 偽類 / 屬性 | 0-0-1-0 |
| 中 |
Element / 偽元素 | 0-0-0-1 |
| 低 |
<a class="a b">HELLO</a>
.a { color: red; } /* 優先級:0-0-1-0 */
.b { color: blue; } /* 優先級:0-0-1-0 */
結果:藍色
原因:兩個選擇器優先級相同,.b 在後面所以覆蓋 .a developer.mozilla
<a class="a b">HELLO</a>
a.b { color: blue; } /* 優先級:0-0-1-1 (1個元素+1個類別) */
.a { color: red; } /* 優先級:0-0-1-0 (1個類別) */
結果:藍色
原因:a.b 優先級較高,不管 CSS 出現順序 developer.mozilla
題目:
<a class="a b">HELLO</a>
a{ color:red }
b{ color:blue }
拆解過程:
HTML 解讀:這是一個 <a> 標籤,同時擁有兩個類別:a 和 b
CSS 解讀:
a{ color:red } 是元素選擇器,選中所有 <a> 標籤 ✓
b{ color:blue } 是元素選擇器,選中所有 <b> 標籤(如 <b>粗體</b>) ✗
結果:只有第一條規則套用,顯示紅色
正確寫法:
a{ color:red } /* 選中 <a> 標籤 */
.b{ color:blue } /* 選中 class="b" 的元素 */
這時兩條規則都會套用,.b 優先級較高,最終顯示藍色。
/* ✗ 錯誤:這是選 <menu> 標籤,不是 class="menu" */
menu { color: red; }
/* ✓ 正確:這才是選 class="menu" */
.menu { color: red; }
b { color: blue; } /* 選 <b> 標籤 */
.b { color: blue; } /* 選 class="b" */
<!-- 這兩個完全一樣 -->
<div class="a b"></div>
<div class="b a"></div>
CSS 規則的出現順序才會影響層疊結果,HTML 中 class 的順序無關。
.a.b { } /* 同時擁有 a 和 b 類別的元素 */
.a .b { } /* a 類別元素裡面的 b 類別元素 */
**Normal Flow(正常流)**是瀏覽器排版的預設模式,所有「佔空間」的元素都在這個流程裡排隊。在 normal flow 中,元素只有兩種排列方式:block(區塊級) 或 inline(行內級)。 docs3.w3cub
Block 元素會「獨佔一整行」,一個接一個垂直往下堆疊。 udn.realityripple
<div>第一個區塊</div>
<p>第二個區塊</p>
<div>第三個區塊</div>
視覺結果:
每個 block 元素自動換行,就算內容很短也會佔滿父元素的寬度。 udn.realityripple
特性 | 說明 |
|---|---|
寬度 | 預設佔滿父元素 100% 寬度 |
高度 | 由內容撐起,可設定 |
可設定尺寸 |
|
可設定留白 |
|
換行 | 前後都會自動換行 |
<div> - 通用容器
<p> - 段落
<h1> ~ <h6> - 標題
<ul> <ol> <li> - 列表
<section> <article> <header> <footer> - 語意化容器
Inline 元素會「在同一行內橫向排列」,像文字一樣左右相鄰,遇到容器邊界才換行。 udn.realityripple
<span>第一段</span>
<strong>第二段</strong>
<a href="#">連結</a>
視覺結果:
所有 inline 元素會擠在同一行,除非容器寬度不夠才會自動換行。 udn.realityripple
特性 | 說明 |
|---|---|
寬度 | 只佔內容所需的寬度 |
高度 | 由內容撐起,無法設定 |
可設定尺寸 |
|
可設定留白 |
|
換行 | 不會自動換行,和其他 inline 元素排在同一行 |
<span> - 通用行內容器
<a> - 超連結
<strong> <em> - 文字強調
<img> - 圖片(特殊的 inline 元素)
<input> <button> - 表單元素
垂直方向一個接一個堆疊
上下 margin 會發生「margin collapsing(合併)」
寬度預設是父元素的 100%
水平方向左右並排
遵循文字排版規則(對齊基線、響應 text-align)
寬度由內容決定,無法手動設定
<!-- ✅ 正確:Block 包 Inline -->
<div>
這是段落,裡面有 <span>行內元素</span>。
</div>
<!-- ❌ 錯誤:Inline 包 Block -->
<span>
<div>區塊元素</div> <!-- 語意錯誤,瀏覽器可能修正 -->
</span>
雖然 HTML 元素有預設的 block/inline 屬性,但可以用 CSS 的 display 改變: udn.realityripple
/* 把 inline 元素改成 block */
span {
display: block; /* 現在 span 會獨佔一行 */
width: 200px; /* 可以設定寬度了 */
}
/* 把 block 元素改成 inline */
div {
display: inline; /* 現在 div 會並排 */
width: 200px; /* 無效,因為 inline 不能設定寬度 */
}
值 | 行為 | 適用情境 |
|---|---|---|
| 獨佔一行,可設定寬高 | 容器、區塊佈局 |
| 並排同行,不可設定寬高 | 文字樣式、小標籤 |
| 並排同行,但可設定寬高 | 按鈕、卡片並排 |
| 完全隱藏,不佔空間 | 條件顯示 |
display: inline-block 是特殊的混合模式: udn.realityripple
.button {
display: inline-block;
width: 100px; /* 可以設定寬高(像 block) */
height: 40px;
padding: 10px; /* 上下 padding 有效 */
margin: 10px; /* 上下 margin 有效 */
/* 但會和其他元素並排(像 inline) */
}
特性:
對外:像 inline 一樣橫向排列,不會換行
對內:像 block 一樣可以設定寬高和四方向留白
常見用途:導覽列按鈕、圖標並排、響應式卡片佈局。
CSS 的「佔空間 / 不佔空間」,其實是在講元素有沒有參與正常排版流程(normal flow)。 udn.realityripple
在 normal flow 裡,元素會跟其他元素「排隊、互相推擠」。 udn.realityripple
一個 block 元素(例如 <div>) 會一個接一個往下堆疊
調整它的 margin、padding、height 時,其他元素會跟著被推開或被擠下去
這種情況,我們就說:這個元素有佔版面空間,是 in flow 的元素
當元素被設成 position: absolute 或 position: fixed 時,會被移出 normal flow。 udn.realityripple
瀏覽器排版其他元素時,完全當它不存在
它原本在 normal flow 要站的位置,會被後面的元素填補上來
它的位置改由 top / right / bottom / left(配合最近的定位父元素或視窗)決定
當多張便利貼重疊時,z-index 決定誰在上面、誰在下面。
.note-1 {
position: absolute;
z-index: 1; /* 比較低,會在下層 */
}
.note-2 {
position: absolute;
z-index: 10; /* 數字越大越上層 */
}
數字越大,越在上面。可以用負數讓元素沉到更下層。
z-index 只對有設定 position 的元素才會作用(除了 position: static)。
/* ❌ 無效:沒有設定 position */
.box {
z-index: 999; /* 不會有任何效果 */
}
/* ✅ 有效:搭配定位屬性 */
.box {
position: relative; /* 或 absolute / fixed / sticky */
z-index: 999; /* 現在才有效 */
}
當父元素設定了 position + z-index 時,會建立一個堆疊上下文(stacking context)。
關鍵規則:子元素的 z-index 只在父元素的群組內比較,無法跨群組競爭。
只會發生在垂直的上下 margin,而且元素要在 normal flow 的 block 排版裡。 developer.mozilla
<p class="a">段落 A</p>
<p class="b">段落 B</p>
.a {
margin-bottom: 20px;
}
.b {
margin-top: 40px;
}
你可能以為 A 和 B 之間距離 = 20 + 40 = 60px,
實際上只會是 40px(較大的那個),這就叫「margin 重疊(collapsing)」。 developer.mozilla
規則:同一層、彼此相鄰、都是 block、沒有東西隔開(沒有邊框、padding、別的元素),上下 margin 就會合成一個,取最大值。 geeksforgeeks
<div class="parent">
<p class="child">文字</p>
</div>
.parent {
margin-top: 50px;
/* 沒有 padding-top、border-top、高度/內容 */
}
.child {
margin-top: 30px;
}
這時你畫面最上方看到的外距不是 50 + 30
而是 max(50, 30) = 50px,等於子元素的 margin 被「傳到」父元素外面去了
.box {
margin-top: 30px;
margin-bottom: 50px;
/* 沒有內容、沒有 padding、沒有 border、高度自動 */
}
這個 .box 的上、下 margin 會互相重疊,變成一個 50px 的外距(取較大)。 developer.mozilla
左右方向的 margin(永遠不會重疊) geeksforgeeks
display: flex 或 grid 裡的子元素 margin developer.mozilla
position: absolute / position: fixed / 浮動元素的 margin developer.mozilla
被 padding、border 或非空內容隔開時 joshwcomeau
可以用「書頁排版」來比喻 margin 的設計思路:
在排版一頁文章時,兩個段落之間通常會設計一個「段落間距」,而不是「上一段底下 10pt + 下一段上面 10pt = 合起來 20pt」,否則越多段落,間距越誇張。 joshwcomeau
CSS 設計者選擇:「如果上下兩個 margin 碰在一起,就把它視為一個段落間距」→ 所以只取最大那個,不做相加。 joshwcomeau
實體排版如果一個段落包在框裡(例如卡片),你通常希望「外框 + 內文」整體看起來的段落間距還是符合版面設計,而不是框裡的 margin 又加一層父容器 margin。 developer.mozilla
margin 重疊只發生在「垂直、block、normal flow」這個世界裡。 geeksforgeeks
如果你看到「為什麼我設很多 margin,間距卻沒想像中大」,90% 是重疊在一起了。
要「打斷」重疊,可以在中間加任何一層「墊片」:
padding
border
實際內容(哪怕是一個看不到的 ::before)
或把那個容器變成 flex/grid
HTML/CSS 和 JavaScript 屬於完全不同類型的語言,這決定了它們的錯誤處理機制和除錯方式。
特性 | HTML/CSS | JavaScript |
|---|---|---|
語言類型 | 宣告式(Declarative) | 命令式(Imperative) |
錯誤處理 | 忽略無效規則,繼續執行 | 拋出錯誤,中斷執行 |
IDE 支援 | 靠規則比對提示 | 直接從引擎獲得錯誤 |
除錯方式 | 視覺檢查 + DevTools | Console 錯誤訊息 + Debugger |
你只是「描述想要的結果」,瀏覽器會盡力實現,遇到錯誤不會中斷執行。
比喻:像「許願清單」,店員盡量滿足,但缺貨時會跳過繼續處理下一項,不會因為一個錯誤就罷工。
/* 瀏覽器遇到無效屬性會直接忽略,繼續套用其他規則 */
.box {
colur: red; /* 拼字錯誤 → 瀏覽器忽略 */
color: blue; /* 正確語法 → 套用 ✓ */
widht: 100px; /* 拼字錯誤 → 瀏覽器忽略 */
height: 100px; /* 正確語法 → 套用 ✓ */
}
CSS 規範明確規定:瀏覽器必須忽略無效的屬性/值,不會拋出任何錯誤訊息。這是設計選擇,確保即使開發者寫錯,使用者仍能看到網頁內容(容錯性 fault tolerance)。
你下達「執行步驟」,引擎必須逐行執行,遇到錯誤會立即拋出例外並中斷。
比喻:像「煮菜食譜」,每一步都要正確執行,缺了鹽就會報錯停止,不會繼續煮下去。
// 1. 語法錯誤(SyntaxError) - 執行前就會偵測
const user = nul; // ❌ SyntaxError: Unexpected token
console.log(user); // 這行不會執行
// 2. 執行時錯誤(Runtime Error)
const user = null;
console.log(user.name); // ❌ TypeError: Cannot read property 'name' of null
// 3. 錯誤捕捉機制
try {
riskyOperation();
} catch (error) {
console.error('捕捉到錯誤:', error.message);
// 程式可以繼續執行
}
用途:找出「為什麼我寫的樣式沒生效」
/* 在 Styles 面板會看到: */
.button {
color: blue; /* 被刪除線蓋掉 */
}
#submit-btn {
color: red; /* ✓ 生效,因為 ID 權重更高 */
}
操作:被刪除線劃掉的屬性就是被覆蓋的規則。
用途:解釋「為什麼這條規則贏了」
權重計算規則: developer.mozilla
(a, b, c)
a = ID 選擇器數量
b = class/屬性/偽類選擇器數量
c = 標籤選擇器數量
操作:滑鼠懸停在選擇器上,DevTools 會顯示權重計算。
用途:看到最終生效的值(經過繼承、覆蓋、預設值計算後)
使用時機:當 Styles 面板規則太多看不出問題時,直接看 Computed 找到最終值,再點擊展開追蹤來源。
用途:快速試錯,不用重新載入
實務流程:
右鍵點擊元素 → 檢查
在 Styles 面板點擊屬性值
修改數值,瀏覽器立即套用
確認效果後,回去改原始 CSS 檔案
注意:DevTools 的修改是暫時的,重新整理會消失!
Box Model 檢視器:查看元素實際的 margin、border、padding、content
Flexbox/Grid 檢視器:顯示 flex items 對齊狀況、grid cells 佈局
偽類狀態切換:強制觸發 :hover、:focus、:active 等狀態
Normal Flow(正常流):元素預設的排版系統 udn.realityripple
Block Formatting Context:block 元素垂直堆疊的規則 udn.realityripple
Inline Formatting Context:inline 元素水平並排的規則 udn.realityripple
Block = 獨佔一行的框:可設定寬高,垂直堆疊
Inline = 並排的文字流:不可設定寬高,水平排列
Inline-Block = 並排的框:兼具兩者優點
只要設定以下屬性,元素就會離開 normal flow: udn.realityripple
position: absolute 或 fixed
float: left 或 right
display: flex 或 grid(子元素會進入新的排版上下文)
筆記時間:2026-02-06
核心學習:CSS 選擇器語法、層疊順序、Normal Flow、Position、Margin Collapsing、HTML/CSS vs JavaScript 除錯差異
學習階段:Phase 2 - JavaScript 基礎 + CSS 切版
Share Dialog
Share Dialog
No activity yet