在填寫寄送地址或商品分類搜尋時,「選了縣市後,行政區選單才會跟著更新」是不可或缺的互動功能。如果沒有做好層級連動,不僅使用者體驗差,還容易讓使用者選出「台北市西屯區」這種邏輯不通的無效資料。
核心觀念與語法拆解
change 事件監聽:捕捉 <select> 值的變更,搭配 Key-Value 結構的資料物件 (districtData[cityKey]) 快速撈出對應的子集陣列。<option>,接著透過 createElement 重建選項,並靈活運用 disabled 屬性控制選單的可用狀態。1. change 事件 vs 資料結構驅動 (Tree Data Structure)
為什麼選用 change 事件?
下拉選單(<select>)只有在使用者切換並選取了新的選項(<option>)時,才會觸發 change 事件。這與文字輸入框用的 input 事件不同,非常適合用來捕捉選單層級的切換動作。
這個機制在幹嘛?
當第一層選單改變時,以第一層選取的 value 作為 Key,去查尋樹狀物件資料(JSON / Object),取出對應的陣列並動態重構第二層選單的 <option>。
2. 動態選單重置與 innerHTML = ""
為什麼需要重置?
每次第一層選單切換時,第二層選單必須先「清空舊選項」,再重新填充新選項。同時,如果第一層切換回預設預設值(例如「請選擇縣市」),第二層選單應同步鎖定(disabled = true)。
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>第十七課 - 多層級聯動下拉選單</title>
<style>
body { font-family: sans-serif; padding: 40px; background-color: #f8f9fa; }
.card { max-width: 450px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.08); }
.form-group { margin-bottom: 20px; }
label { font-weight: bold; color: #333; display: block; margin-bottom: 8px; }
/* 下拉選單基本樣式 */
select {
width: 100%;
padding: 10px;
font-size: 15px;
border: 2px solid #ced4da;
border-radius: 6px;
outline: none;
background-color: white;
cursor: pointer;
transition: border-color 0.2s ease;
}
select:focus { border-color: #007bff; }
select:disabled { background-color: #e9ecef; cursor: not-allowed; }
/* 結果顯示區 */
.result-box {
margin-top: 20px;
padding: 12px;
background-color: #e7f5ff;
border-left: 4px solid #007bff;
color: #004085;
font-weight: bold;
display: none;
}
.result-box.show { display: block; }
</style>
</head>
<body>
<div class="card">
<h2>寄送地址選取</h2>
<!-- 第一層:縣市選單 -->
<div class="form-group">
<label for="citySelect">選擇縣市</label>
<select id="citySelect">
<option value="">-- 請選擇縣市 --</option>
<option value="taipei">台北市</option>
<option value="taichung">台中市</option>
<option value="kaohsiung">高雄市</option>
</select>
</div>
<!-- 第二層:鄉鎮市區選單 (預設停用) -->
<div class="form-group">
<label for="districtSelect">選擇行政區</label>
<select id="districtSelect" disabled>
<option value="">-- 請先選擇縣市 --</option>
</select>
</div>
<!-- 選擇結果顯示區 -->
<div id="resultBox" class="result-box"></div>
</div>
<script>
// ----------------------------------------------------
// JavaScript 邏輯 (多層級聯動與 change 事件)
// ----------------------------------------------------
// 1. 定義區域階層資料 (模擬資料庫或 API 傳回的關聯物件)
const districtData = {
taipei: ["中正區", "大安區", "信義區", "中山區"],
taichung: ["西屯區", "北區", "南屯區", "豐原區"],
kaohsiung: ["新興區", "前鎮區", "左營區", "苓雅區"]
};
// 2. 取得 DOM 元素
const citySelect = document.getElementById("citySelect");
const districtSelect = document.getElementById("districtSelect");
const resultBox = document.getElementById("resultBox");
// 3. 監聽第一層縣市選單的 change 事件
citySelect.addEventListener("change", function() {
const selectedCityKey = citySelect.value; // 取得選取的縣市 Key (例如 "taipei")
// 清空第二層選單的舊內容
districtSelect.innerHTML = '<option value="">-- 請選擇行政區 --</option>';
resultBox.classList.remove("show");
// 判斷是否有選取有效縣市
if (selectedCityKey && districtData[selectedCityKey]) {
// 取得對應的行政區陣列
const districts = districtData[selectedCityKey];
// 走訪陣列,動態建立第二層 <option> 節點
districts.forEach(function(districtName) {
const option = document.createElement("option");
option.value = districtName;
option.innerText = districtName;
districtSelect.appendChild(option);
});
// 啟用第二層選單
districtSelect.disabled = false;
} else {
// 若選回預設的空白選項,重置並停用第二層選單
districtSelect.innerHTML = '<option value="">-- 請先選擇縣市 --</option>';
districtSelect.disabled = true;
}
});
// 4. 監聽第二層行政區選單的 change 事件
districtSelect.addEventListener("change", function() {
const selectedCityName = citySelect.options[citySelect.selectedIndex].text;
const selectedDistrict = districtSelect.value;
if (selectedDistrict) {
resultBox.innerText = `您選擇的區域為:${selectedCityName} ${selectedDistrict}`;
resultBox.classList.add("show");
} else {
resultBox.classList.remove("show");
}
});
</script>
</body>
</html>
今天我們掌握了多層級資料對應與選單連動的核心技術。這種「父層改動 ➔ 撈取對應資料 ➔ 清空與重建子層 DOM」的模式,可以輕鬆延伸至三層(縣市 ➔ 行政區 ➔ 路段)、電商多重規格選單(顏色 ➔ 尺寸 ➔ 庫存)等複雜場景!
在購物網站瀏覽商品時,常常有「關鍵字搜尋 + 價格區間 + 分類」等多個篩選條件同時作用,該怎麼做?
【第十八課】資料檢索:多條件即時搜尋與篩選器(Array.filter 與複合狀態監控),我們將學習如何運用 JavaScript 陣列高階方法打造強大的前端篩選器。