# 四、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>
```
##### 範例展示

---
#### 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;
}
```
> ✅ **最佳實務**:結構與樣式分離,易於維護與重複使用。
##### 檔案結構

##### 範例截圖

---
### 在 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>
```
> ⚠️ **不推薦**:邏輯分散,難以除錯與維護。
##### 範例截圖

---
#### 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!');
});
```
> ✅ **最佳實務**:結構、樣式、邏輯完全分離。
##### 檔案結構

##### 範例截圖

---
### 載入順序的重要性
1. **CSS 放在 `<head>`**:確保網頁解析時就已經有樣式,避免「內容閃爍」。
2. **JavaScript 放在 `<body>` 結尾**:確保 HTML 元素(DOM)已經載入完成,程式碼才能正確抓取並操作元素。

<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** (外距)

```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; /* 讓你設多少寬度就是多少 */
}
```

### 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 = '隱藏答案';
}
});
```
| 點擊前 | 點擊後 |
| -------- | -------- |
|  |  |
---
### 專案二:動態待辦清單 (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();
}
});
```
| 操作前 | 操作後 |
| -------- | -------- |
|  |  |
---
### 最佳實務總結
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>