级联面板本质是状态联动而非dom嵌套,核心在于上一级选中值决定下一级可选项,需每次选择后清空后续所有层级的value和options,并用扁平化数据结构(如映射表)实现o(1)查询。

级联面板本质是状态联动,不是 DOM 嵌套
很多人一上来就想着用 <select></select> 套 <select></select> 或者手动写多层 div 展开收起,结果逻辑越写越乱。级联的核心在于「上一级选中值决定下一级可选项」,和 UI 展开方式无关——你可以用下拉框、按钮组、甚至卡片点击来实现。关键在数据驱动:每次选择后,清空后续所有层级的选项,并根据当前值重新生成下一级 options 或 items。
常见错误现象:第二次选省之后,市列表没更新、点回北京,区列表还是上次上海的、选完三级后修改二级,三级没清空还残留旧数据。这些问题几乎都源于没做「下游状态重置」。
- 每次
change事件触发后,必须清空后续所有层级的value和options - 不要直接操作
innerHTML拼接option标签,优先用document.createElement('option')或数组映射 +innerHTML = options.map(...).join('') - 如果用原生 JS,记得给每个
select绑定独立的id或data-level,避免用querySelectorAll('select')[1]这类脆弱索引
用原生 HTML + JS 实现三级省市区,注意数据结构扁平化
别把省市区数据存成三层嵌套对象(比如 {'北京市': {'朝阳区': [...], '海淀区': [...]}}),查起来慢、改起来难。推荐用两个映射表:provinces(省列表)、citiesByProvinceCode(以省 code 为 key 的市列表),区同理。这样可以 O(1) 查到下一级数据。
示例片段(简化版):
const provinces = [
{ code: '110000', name: '北京市' },
{ code: '310000', name: '上海市' }
];
const citiesByProvinceCode = {
'110000': [{ code: '110100', name: '市辖区' }],
'310000': [{ code: '310100', name: '市辖区' }]
};
// 选省后:
selectCity.innerHTML = '';
const cityList = citiesByProvinceCode[provinceSelect.value] || [];
cityList.forEach(city => {
const opt = document.createElement('option');
opt.value = city.code;
opt.textContent = city.name;
selectCity.appendChild(opt);
});
// 别忘了:selectDistrict.innerHTML = ''; selectDistrict.value = '';
Vue/React 里级联更简单,但别滥用 v-model 双向绑定
在 Vue 中用 v-model 绑定 selectedProvince 很自然,但容易忽略「依赖链断裂」:比如 computed 的 cityOptions 依赖 selectedProvince,但没监听 selectedProvince 变化时主动清空 selectedCity。React 同理,useEffect 里只处理渲染,不处理重置。
- Vue:在
watch里监听selectedProvince,一旦变,就this.selectedCity = ''、this.selectedDistrict = '' - React:用
useEffect监听selectedProvince,执行setSelectedCity('')和setSelectedDistrict('') - 不要把「获取城市列表」写在
render或模板中(如v-for="c in getCities(selectedProvince)"),这会导致重复计算;提前算好或用 memo
移动端点透、滚动穿透、键盘弹出干扰怎么防
在 iOS Safari 或微信内置浏览器里,级联面板常遇到:点完省,键盘还没收,市下拉框就盖不住键盘;或者点选市时,底层页面跟着滚动。这不是逻辑问题,是交互细节没控住。
- 所有
select元素加onblur="this.blur()"防止失焦后仍聚焦导致键盘不收 - 展开某一层时,给
body加style="position: fixed; width: 100%"锁定滚动(记得收起时移除) - 避免在
select外层再包一层div并设overflow: hidden—— 这在某些安卓 WebView 里会截断下拉菜单 - 真要自定义下拉面板(非原生
select),务必加tabindex="-1"和role="listbox",否则无障碍不可用
select 的 focus 行为异常。留个心眼:每次选完,打印下所有层级的 value,比猜强。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











