# 四、CSS 與 JavaScript 整合入門 ## 4.1 HTML 與前端整合入門 ### 前端三劍客的協作關係 在第一章我們曾介紹,現代網頁由三種技術構成: - **HTML**:結構與內容(骨架) - **CSS**:外觀與樣式(皮膚) - **JavaScript**:互動與邏輯(大腦) 這三者不是獨立運作,而是緊密協作。 > 💡 比喻: > - HTML = 房子的鋼筋水泥結構 > - CSS = 裝潢、油漆、家具風格 > - JavaScript = 智慧家庭系統(開關燈、自動溫控) --- ### 在 HTML 中引入 CSS 的三種方式 #### 1. 內嵌樣式 (Inline Style) 直接在標籤內使用 `style` 屬性: ```html <p style="color: blue; font-size: 18px;">這是藍色文字</p> ``` > ⚠️ **不推薦**:難以維護,樣式與結構混在一起。 ##### 完整範例 ```htmlembedded= <!DOCTYPE html> <html lang="zh-Hant"> <head> <meta charset="UTF-8" /> <title>我的網站</title> </head> <body> <p style="color: blue; font-size: 18px;">這是藍色文字</p> </body> </html> ``` ##### 範例展示 ![截圖 2026-01-07 下午3.26.32](https://hackmd.io/_uploads/H1vAqFsVbx.png) --- #### 2. 內部樣式表 (Internal CSS) 在 `<head>` 區塊內使用 `<style>` 標籤: ##### 完整範例 ```html <!DOCTYPE html> <html lang="zh-Hant"> <head> <meta charset="UTF-8" /> <title>我的網站</title> <style> p { color: blue; font-size: 18px; } </style> </head> <body> <p>這是藍色文字</p> </body> </html> ``` > 💡 適合單頁小型網站或原型測試。 --- #### 3. 外部樣式表 (External CSS) - 推薦 將 CSS 獨立成 `.css` 檔案,再用 `<link>` 引入: **index.html:** ```html <head> <link rel="stylesheet" href="style.css"> </head> ``` **style.css:** ```css p { color: blue; } ``` > ✅ **最佳實務**:結構與樣式分離,易於維護與重複使用。 ##### 檔案結構 ![具有兩份檔案](https://hackmd.io/_uploads/SJv6oYiNZx.png) ##### 範例截圖 ![完整範例結構](https://hackmd.io/_uploads/S1QasFiE-g.png) --- ### 在 HTML 中引入 JavaScript 的方式 #### 1. 內嵌 JavaScript (Inline) 直接在標籤內使用事件屬性: ##### 完整範例 ```html <!DOCTYPE html> <html lang="zh-Hant"> <head> <meta charset="UTF-8" /> <title>我的網站</title> </head> <body> <body> <button onclick="alert('Hello!')">點我</button> </body> </body> </html> ``` > ⚠️ **不推薦**:邏輯分散,難以除錯與維護。 ##### 範例截圖 ![截圖 2026-01-07 下午3.32.32](https://hackmd.io/_uploads/H1pSntiVWl.png) --- #### 2. 內部 JavaScript 在 HTML 內使用 `<script>` 標籤: ##### 完整範例 ```html <!DOCTYPE html> <html lang="zh-Hant"> <head> <meta charset="UTF-8" /> <title>我的網站</title> </head> <body> <button id="myBtn">點我</button> <script> document.getElementById('myBtn').onclick = function () { alert('Hello!'); }; </script> </body> </html> ``` --- #### 3. 外部 JavaScript - 推薦 將 JavaScript 獨立成 `.js` 檔案: **index.html:** ```html <body> <button id="myBtn">點我</button> <script src="script.js"></script> </body> ``` **script.js:** ```javascript document.getElementById('myBtn').addEventListener('click', function() { alert('Hello!'); }); ``` > ✅ **最佳實務**:結構、樣式、邏輯完全分離。 ##### 檔案結構 ![截圖 2026-01-07 下午3.44.36](https://hackmd.io/_uploads/S1qXycjNZx.png) ##### 範例截圖 ![截圖 2026-01-07 下午3.44.51](https://hackmd.io/_uploads/r1MNkcjV-x.png) --- ### 載入順序的重要性 1. **CSS 放在 `<head>`**:確保網頁解析時就已經有樣式,避免「內容閃爍」。 2. **JavaScript 放在 `<body>` 結尾**:確保 HTML 元素(DOM)已經載入完成,程式碼才能正確抓取並操作元素。 ![截圖 2026-01-07 下午5.04.32](https://hackmd.io/_uploads/SJJrGsiVWe.png) <br/> ## 4.2 基礎 CSS 入門學習 ### CSS 語法結構 ```css 選擇器 { 屬性: 值; 屬性: 值; } ``` **範例:** ```css h1 { color: red; font-size: 24px; } ``` ### 常見選擇器 1. **元素選擇器 (Tag Selector)**:選取所有特定標籤。 ```css p { color: gray; } ``` 2. **類別選擇器 (Class Selector)**:選取有特定 Class 的元素 (最常用)。 ```css .highlight { background-color: yellow; } ``` 3. **ID 選擇器 (ID Selector)**:選取特定 ID 的唯獨元素。 ```css #header { padding: 20px; } ``` ### 盒模型 (Box Model) 每個元素都是一個盒子,由內而外包含: 1. **Content** (內容) 2. **Padding** (內距) 3. **Border** (邊框) 4. **Margin** (外距) ![盒模型 (Box Model)](https://hackmd.io/_uploads/SkiFOoj4be.jpg) ```css .card { width: 300px; padding: 20px; /* 內容與邊框的距離 */ border: 1px solid #ddd; margin: 30px; /* 盒子與外部的距離 */ } ``` #### 2. 盒子的尺寸計算:`box-sizing` 這是一個新手常遇到的坑:**設定了寬度 100px,加了內距 20px,結果盒子變成了 140px?** 這是因為 CSS 預設的計算方式 (`content-box`) 是: > 總寬度 = width + padding + border 為了直覺好管理,現代網頁開發習慣將它改為 `border-box`: > 總寬度 = width (padding 和 border 會被包在裡面,不會撐大盒子) **✅ 最佳實務:全域重置** 建議在所有 CSS 檔案的最開頭加上這段程式碼: ```css *, *::before, *::after { box-sizing: border-box; /* 讓你設多少寬度就是多少 */ } ``` ![box-sizing](https://hackmd.io/_uploads/SkFbsoiEZx.jpg) ### CSS 常用屬性與單位速查 在排版之前,我們先熟悉幾個最常用的 CSS 設定: #### 1. 尺寸單位 (Units) 網頁上不只有 `px` (像素),還有依比例縮放的單位。 * **`px` (像素)**:絕對單位,固定大小。適合:邊框粗細、最小寬度。 * `width: 200px;` * **`%` (百分比)**:主要相對於**父元素**的大小。適合:響應式寬度。 * `width: 50%;` (父元素的一半寬) * **`rem`**:相對於**根元素 (html)** 的字體大小。適合:字體大小、間距。 * `font-size: 1.2rem;` (若預設是 16px,1.2rem 就是 19.2px) > 💡 **小撇步**:現在主流建議字體大小用 `rem`,這樣使用者調整瀏覽器預設字體時,網頁才會跟著放大,符合無障礙設計! #### 2. 文字排版 (Typography) 讓文字更好讀的關鍵屬性。 ```css p { font-family: Arial, sans-serif; /* 字體:先找 Arial,沒有就用系統無襯線字體 */ font-weight: bold; /* 粗細:bold (粗體) 或 normal (正常) */ line-height: 1.6; /* 行高:建議 1.5 ~ 1.8 倍,太擠很難讀 */ text-align: center; /* 對齊:left (預設), center, right */ color: #333; /* 顏色:建議不要用純黑 (#000),用深灰較不刺眼 */ } ``` #### 3. 背景處理 (Background) ```css .banner { background-color: #f0f0f0; /* 背景色 */ background-image: url('bg.jpg'); /* 背景圖 */ background-size: cover; /* ⭐️ 重要:讓圖片自動填滿且裁切 */ background-position: center; /* 圖片居中 */ } ``` #### 4. 邊框與游標 (Border & Cursor) 讓介面更精緻、互動性更好的細節: * **`border-radius` (圓角)**:讓方形變圓潤。 * `border-radius: 10px;` (微圓角) * `border-radius: 50%;` (變圓形,常用於頭像) * **`cursor` (游標型態)**:當滑鼠移上去時的樣子。 * `cursor: pointer;` (變成手指形狀,表示可點擊,按鈕必備!) * `cursor: not-allowed;` (禁止符號) #### 5. 顯示模式 (Display) HTML 標籤天生有兩種個性: | 模式 | 特性 | 常見標籤 | | :--- | :--- | :--- | | **`block` (區塊)** | 獨佔一行,可以設定寬高 | `<div>`, `<p>`, `<h1>`, `<section>` | | **`inline` (行內)** | 和其他人擠在同一行,**不能**設定寬高 | `<span>`, `<a>` | | **`inline-block`** | 擠在同一行,但**可以**設定寬高 | `<img>`, `<button>` | | **`none` (隱藏)** | 元素完全消失,不佔據空間 | (常用於互動顯示/隱藏) | > 如果你想讓 `<a>` 連結變成一顆有寬高的大按鈕,記得設 `display: inline-block;` 或 `display: block;`! ```css a.button { display: inline-block; /* 這樣才能設定 padding 和 width */ padding: 10px 20px; } ``` ### Flexbox 排版基礎 **Flexbox (彈性盒模型)** 是現代 CSS 最重要的排版系統,專門用來解決「對齊」與「分佈」的問題。 要精通 Flexbox,你只需要掌握兩個角色和三個關鍵屬性。 #### 1. 兩個角色:容器與項目 * **Flex Container (容器)**:外層的爸爸。只要設了 `display: flex;`,它就變成了 Flex 容器。 * **Flex Item (項目)**:內層的兒子。容器內的**直屬**子元素會自動變成 Flex 項目,聽從容器的指揮進行排列。 #### 2. 決定排列方向:`flex-direction` (主軸方向) Flexbox 預設是「橫向」排列,但你可以改成「直向」。這條排列的線,我們稱為**主軸 (Main Axis)**。 ```css .container { display: flex; /* 決定主軸方向 */ flex-direction: row; /* (預設值) 橫向排列:從左到右 */ /* flex-direction: column; 直向排列:從上到下 */ /* flex-direction: row-reverse; 橫向反轉:從右到左 */ } ``` > 💡 **重要觀念**:「主軸」是你設定的方向,「交錯軸」則是跟它垂直的那個方向。 #### 3. 主軸對齊:`justify-content` 決定項目在「主軸」上如何分佈 (例如:靠左、居中、分散)。 ```css .container { display: flex; justify-content: flex-start; /* (預設) 靠起點對齊 (通常是左邊) */ justify-content: center; /* 居中對齊 */ justify-content: flex-end; /* 靠終點對齊 (通常是右邊) */ justify-content: space-between; /* 左右貼齊邊緣,中間平均分配空白 (導覽列最常用!) */ justify-content: space-around; /* 每個項目周圍都有相等空白 */ } ``` #### 4. 交錯軸對齊:`align-items` 決定項目在「交錯軸」(垂直於主軸的方向) 上如何對齊 (例如:上下置中)。 ```css .container { display: flex; align-items: stretch; /* (預設) 拉伸:自動填滿容器高度 */ align-items: center; /* 置中:垂直居中 (最常用!) */ align-items: flex-start; /* 靠上對齊 */ align-items: flex-end; /* 靠下對齊 */ } ``` #### 5. 自動換行:`flex-wrap` 預設情況下,Flexbox 會強迫所有項目擠在同一行 (就算擠爆也不換行)。如果你希望滿了就自動換到下一行,要開啟這個屬性。 ```css .container { display: flex; flex-wrap: nowrap; /* (預設) 不換行,硬擠 */ flex-wrap: wrap; /* 自動換行,適合做卡片列表 */ } ``` #### 🚀 Flexbox 速查範例:完美置中 這是面試最常考、開發最常用的程式碼: ```css .center-box { display: flex; justify-content: center; /* 水平置中 */ align-items: center; /* 垂直置中 */ height: 300px; /* 需有高度才看得到垂直置中效果 */ } ``` <br/> ## 4.3 基礎 JavaScript 入門學習 ### JavaScript 能做什麼? - **DOM 操作**:修改網頁內容或樣式。 - **事件監聽**:偵測點擊、輸入、滾動等行為。 ### 變數宣告 推薦使用 `const` (常數) 與 `let` (變數)。 ```javascript const myName = "小明"; // 不會變的資料 let count = 0; // 會變動的資料 ``` ### 函式 (Functions) 與 邏輯控制 要想寫出有「邏輯」的程式,這兩個觀念必不可少。 #### 1. 函式 (Function) 將一段程式碼包起來,改個名字,以便重複使用。 ```javascript function sayHello() { alert("你好!"); } // 呼叫函式 sayHello(); ``` #### 2. 條件判斷 (If...Else) 讓程式根據情況做不同的事。 ```javascript if (count > 10) { console.log("數量太多了!"); } else { console.log("數量正常"); } ``` ### 選取 DOM 元素 ```javascript // 透過 ID 選取 const title = document.getElementById('title'); // 透過 CSS 選擇器選取 (推薦) const btn = document.querySelector('.btn-primary'); ``` ### 修改內容與樣式 ```javascript title.textContent = "Hello World"; // 修改文字 title.style.color = "red"; // 修改樣式 (不推薦大量使用) // Class 操作 (推薦) title.classList.add("active"); // 新增 Class title.classList.remove("active"); // 移除 Class title.classList.toggle("active"); // 切換 (有就移除,無就新增) title.classList.contains("active"); // 檢查是否有該 Class (回傳 true/false) // 表單輸入值 // 假設有個 <input id="name"> const input = document.getElementById('name'); console.log(input.value); // 取得使用者輸入的內容 ``` ### 動態新增與移除元素 (Dynamic DOM) 進階應用常用到:用程式碼「無中生有」做出 HTML。 ```javascript // 1. 建立新元素 const newDiv = document.createElement('div'); newDiv.textContent = "我是新來的!"; // 2. 放入畫面中 (加入到 body 的最後面) document.body.appendChild(newDiv); // 3. 移除元素 newDiv.remove(); ``` ### 事件監聽 (Event Listener) ```javascript btn.addEventListener('click', function() { console.log("按鈕被點擊了!"); }); ``` <br/> ## 4.4 前端進階整合應用 現在我們將所學整合,實作兩個常見的小專案。 ### 專案一:互動式問答卡片 #### 功能需求 - 顯示一個問題與「顯示答案」按鈕。 - 點擊按鈕後,展開答案區域,並將按鈕文字改為「隱藏答案」。 - 再次點擊則收起答案。 **HTML:** ```html <div class="qa-card"> <h3>HTML 是什麼縮寫?</h3> <button id="toggleBtn">顯示答案</button> <div id="answer" class="hidden"> <p>HyperText Markup Language</p> </div> </div> ``` **CSS:** ```css .qa-card { border: 1px solid #ccc; padding: 20px; border-radius: 8px; width: 300px; } .hidden { display: none; /* 隱藏元素 */ } ``` **JavaScript:** ```javascript const btn = document.getElementById('toggleBtn'); const answer = document.getElementById('answer'); btn.addEventListener('click', function() { // 切換 hidden class (有就移除,無就新增) answer.classList.toggle('hidden'); // 根據狀態修改按鈕文字 if (answer.classList.contains('hidden')) { btn.textContent = '顯示答案'; } else { btn.textContent = '隱藏答案'; } }); ``` | 點擊前 | 點擊後 | | -------- | -------- | | ![截圖 2026-01-07 下午5.53.49](https://hackmd.io/_uploads/ByFUpsi4Wl.png) | ![截圖 2026-01-07 下午5.54.00](https://hackmd.io/_uploads/B1YU6sj4Zg.png) | --- ### 專案二:動態待辦清單 (Todo List) 比起前一個專案,這個專案需要動態「新增」與「刪除」HTML 元素。 > 💡 這是學習 DOM 操作最重要的練習之一! **HTML:** ```html <div class="todo-app"> <h2>我的待辦清單</h2> <div class="input-group"> <input type="text" id="itemInput" placeholder="輸入待辦事項..."> <button id="addBtn">新增</button> </div> <ul id="list"> <!-- JavaScript 會將項目加在這裡 --> </ul> </div> ``` **CSS:** ```css .todo-app { width: 400px; margin: 20px auto; padding: 20px; border: 1px solid #eee; border-radius: 10px; } .list-item { display: flex; justify-content: space-between; /* 文字靠左,按鈕靠右 */ padding: 10px; border-bottom: 1px solid #eee; } .delete-btn { background-color: #ff4d4d; color: white; border: none; border-radius: 4px; cursor: pointer; } ``` **JavaScript:** ```javascript // 1. 選取元素 const input = document.getElementById('itemInput'); const addBtn = document.getElementById('addBtn'); const list = document.getElementById('list'); // 2. 定義「新增項目」的函式 function addItem() { const text = input.value.trim(); // 取得輸入內容並去除空白 // 如果內容是空的,不執行 if (text === "") { alert("請輸入內容!"); return; } // A. 建立新的 li 元素 const li = document.createElement('li'); li.className = 'list-item'; // B. 建立內容文字 const span = document.createElement('span'); span.textContent = text; // 點擊文字可以切換「完成」樣式 span.addEventListener('click', function() { span.style.textDecoration = span.style.textDecoration === 'line-through' ? 'none' : 'line-through'; }); // C. 建立刪除按鈕 const delBtn = document.createElement('button'); delBtn.textContent = '刪除'; delBtn.className = 'delete-btn'; // 賦予刪除功能 delBtn.addEventListener('click', function() { li.remove(); // 移除這個 li 元素 }); // D. 組裝元素 (把 span 和 button 放進 li) li.appendChild(span); li.appendChild(delBtn); // E. 把 li 放進清單中 list.appendChild(li); // F. 清空輸入框 input.value = ""; input.focus(); } // 3. 綁定事件 addBtn.addEventListener('click', addItem); // 讓 Enter 鍵也能新增 input.addEventListener('keypress', function(e) { if (e.key === 'Enter') { addItem(); } }); ``` | 操作前 | 操作後 | | -------- | -------- | | ![截圖 2026-01-07 下午5.56.12](https://hackmd.io/_uploads/HkfeRjsV-g.png) | ![截圖 2026-01-07 下午5.56.26](https://hackmd.io/_uploads/SyXlRosNZe.png) | --- ### 最佳實務總結 1. **關注點分離**:HTML 負責結構,CSS 負責樣式,JS 負責邏輯。 2. **避免 innerHTML**:使用 `createElement` 和 `textContent` 更安全且效能更好,也能保留事件監聽器。 3. **使用 Class 切換樣式**:盡量不要用 JS 直接改 `style`,而是透過 `classList.toggle()` 來切換 CSS class。 <br/> ## 本章練習題 **1. 在網頁開發的前端三劍客中,負責處理「互動與邏輯」的是哪一項技術?** - A) HTML - B) CSS - C) JavaScript - D) SQL <details> <summary>點選查看答案</summary> 答案: C) JavaScript **解析:** HTML 負責結構,CSS 負責外觀樣式,JavaScript 負責互動與邏輯控制。SQL 則是資料庫查詢語言,屬於後端範疇。 </details> **2. 在 CSS 盒模型 (Box Model) 中,位於「內容 (Content)」與「邊框 (Border)」之間的區域稱為什麼?** - A) Margin (外距) - B) Padding (內距) - C) Outline (輪廓) - D) Width (寬度) <details> <summary>點選查看答案</summary> 答案: B) Padding (內距) **解析:** 由內而外的順序為:Content (內容) -> Padding (內距) -> Border (邊框) -> Margin (外距)。 </details> **3. 若要選取 HTML 中 `<div id="header">` 這個元素,應該使用哪一個 CSS 選擇器?** - A) `.header` - B) `header` - C) `*header` - D) `#header` <details> <summary>點選查看答案</summary> 答案: D) `#header` **解析:** - `#` 用於 ID 選擇器 (對應 `id=""`) - `.` 用於類別選擇器 (對應 `class=""`) - 無符號直接寫標籤名用於元素選擇器 </details> **4. 在 JavaScript 中,建議使用哪一種方式來監聽按鈕的點擊事件?** - A) 在 HTML 中寫 `<button onclick="...">` - B) 在 CSS 中寫 `button:active { ... }` - C) 使用 `element.addEventListener('click', ...)` - D) 使用 `element.click = ...` <details> <summary>點選查看答案</summary> 答案: C) 使用 `element.addEventListener('click', ...)` **解析:** - A) 是內嵌寫法,不推薦。 - B) 是 CSS 偽類,只能改變樣式,不能執行邏輯。 - C) 是標準且推薦的事件監聽方式,可同時綁定多個事件且結構分離。 </details> **5. 若要使用 JavaScript 動態建立一個新的 `<div>` 元素,應該使用哪個方法?** - A) `document.createElement('div')` - B) `document.newElement('div')` - C) `document.add('div')` - D) `document.make('div')` <details> <summary>點選查看答案</summary> 答案: A) `document.createElement('div')` **解析:** `document.createElement(tagName)` 是標準的 DOM API,用於建立新的元素節點。 </details> **6. 為了符合無障礙設計並讓文字大小能隨瀏覽器設定縮放,建議使用哪種單位來設定 `font-size`?** - A) px (像素) - B) rem - C) cm (公分) - D) vh (視窗高度) <details> <summary>點選查看答案</summary> 答案: B) rem **解析:** `rem` 是相對於根元素 (root element) 的字體大小,能響應使用者在瀏覽器設定的預設字體大小,是目前最推薦的文字單位。 </details> **7. 若希望某個元素在網頁上「完全消失」且「不佔據任何空間」,應設定什麼 CSS?** - A) `opacity: 0;` (透明度為 0) - B) `visibility: hidden;` (隱藏可見度) - C) `display: none;` (不顯示) - D) `background-color: transparent;` (背景透明) <details> <summary>點選查看答案</summary> 答案: C) `display: none;` **解析:** - `display: none;` 會讓元素從排版中完全移除,不佔空間。 - `opacity: 0` 和 `visibility: hidden` 雖然看不見,但元素原本的位置仍會被保留 (佔空間)。 </details> **8. 在 `display: flex;` 的容器中,若要將子元素「水平置中」排列,應使用哪個屬性?** - A) `align-items: center;` - B) `text-align: center;` - C) `padding: center;` - D) `justify-content: center;` <details> <summary>點選查看答案</summary> 答案: D) `justify-content: center;` **解析:** 在預設的 Flex 模式下 (橫向排列),`justify-content` 控制主軸 (水平) 的對齊,`align-items` 控制交錯軸 (垂直) 的對齊。 </details> **9. 在 JavaScript 中,若宣告一個變數後「不打算」也「不應該」再修改其數值,應使用哪個關鍵字?** - A) `var` - B) `let` - C) `const` - D) `static` <details> <summary>點選查看答案</summary> 答案: C) `const` **解析:** `const` (constant) 用於宣告常數,一旦賦值後就不能再重新賦值 (Reassign),這能提高程式的穩定性與可讀性。 </details> **10. 若要製作一個按鈕,點擊時能「切換」選單的顯示與隱藏狀態 (有就移除 class,沒有就新增),最推薦使用哪個方法?** - A) `classList.add()` - B) `classList.remove()` - C) `classList.toggle()` - D) `classList.switch()` <details> <summary>點選查看答案</summary> 答案: C) `classList.toggle()` **解析:** `classList.toggle('className')` 會自動偵測:若元素已有該 class 則移除,若沒有則新增,非常適合用於開關式 (On/Off) 的互動功能。 </details>