在 Web 表單中,地址選擇常需「省 → 市 → 區」三級聯動。傳統提交整頁體驗差,採用 AJAX 非同步載入 可實現無刷新動態更新,極大提升使用者體驗。本文示範前端 jQuery + 後端 Spring MVC / Struts2 的完整實現。
單表 areas 即可:
CREATE TABLE areas (
id INT PRIMARY KEY,
name VARCHAR(50),
parent_id INT, -- 關聯上級行政區劃
level TINYINT -- 1=省, 2=市, 3=區
);
public class Area {
private Integer id;
private String name;
private Integer parentId;
// getter/setter 省略
}
@Service
public class AreaService {
@Autowired private AreaMapper areaMapper; // MyBatis Mapper
public List getProvinces() {
return areaMapper.selectByParentIdAndLevel(0, 1);
}
public List getCities(Integer provinceId) {
return areaMapper.selectByParentIdAndLevel(provinceId, 2);
}
public List getDistricts(Integer cityId) {
return areaMapper.selectByParentIdAndLevel(cityId, 3);
}
}
provinces() { return areaService.getProvinces(); }
@GetMapping("/cities")
public List cities(@RequestParam Integer provinceId) {
return areaService.getCities(provinceId);
}
@GetMapping("/districts")
public List districts(@RequestParam Integer cityId) {
return areaService.getDistricts(cityId);
}
}
<div class="form-group">
<label>所在地區</label>
<select id="province" class="form-control"><option value="">請選擇省份</option></select>
<select id="city" class="form-control" disabled><option value="">請選擇城市</option></select>
<select id="district" class="form-control" disabled><option value="">請選擇區縣</option></select>
</div>
$(function() {
// 1. 頁面載入時初始化省份
loadOptions('/api/area/provinces', '#province', null, function() {
$('#province').prop('disabled', false);
});
// 2. 省份變更 → 載入城市
$('#province').change(function() {
const pid = $(this).val();
$('#city, #district').prop('disabled', true).html('');
if (pid) {
loadOptions('/api/area/cities', '#city', { provinceId: pid }, function() {
$('#city').prop('disabled', false);
});
}
});
// 3. 城市變更 → 載入區縣
$('#city').change(function() {
const cid = $(this).val();
$('#district').prop('disabled', true).html('');
if (cid) {
loadOptions('/api/area/districts', '#district', { cityId: cid }, function() {
$('#district').prop('disabled', false);
});
}
});
});
// 通用載入函式
function loadOptions(url, selector, params, callback) {
$.getJSON(url, params || {}, function(data) {
const $sel = $(selector);
$.each(data, function(i, item) {
$sel.append($('
localStorage 或後端用 Redis 快取,減少請求。select 添加 aria-label,確保螢幕閱讀器可讀。若專案仍使用 Struts2,Action 方法需手寫 JSON 序列化(或用 JSON 插件),並通過 HttpServletResponse 輸出:
public void getCities() {
List list = areaService.getCities(provinceId);
String json = new Gson().toJson(list);
ServletActionContext.getResponse().setContentType("application/json;charset=UTF-8");
ServletActionContext.getResponse().getWriter().write(json);
}
前端 AJAX 呼叫方式不變,只需確保 URL 對應到 *.action 或 REST 風格映射。
省市區三級聯動的核心在於:後端提供分層級的 RESTful API,前端透過事件鏈串聯選單狀態。採用 JSON 交換資料、jQuery 簡化 DOM 操作,配合適當的快取與錯誤處理,即可構建流暢、健壯的地址選擇體驗。