纯html无法实现真正的级联选择器,必须依赖javascript控制数据联动与状态重置、css负责样式;select嵌套不等于级联,因其彼此孤立且无依赖感知,需通过事件监听、动态更新、错误处理、扁平数据结构及无障碍支持等完整方案实现。

纯 HTML 实现不了真正的级联选择器(cascader),必须靠 JavaScript 控制数据联动和状态重置,CSS 只管样式。所谓“HTML 实现”,其实是 <select></select> 或 <div> 结构 + JS 逻辑 + CSS 样式三者协作的结果。
<h3>为什么 <code><select></select> 嵌套不等于级联
原生 <select></select> 是孤立控件,彼此无感知。选了“广东省”不会自动清空“北京市朝阳区”——那只是 DOM 里还留着的旧 <option></option>。浏览器根本不理解“省→市→区”的依赖关系。
- 没监听
change事件,上级变化时下游完全无响应 - 不清空下游
innerHTML或调用replaceChildren(),选项会越叠越多 - 不设
disabled,用户可能在数据未返回前就点三级下拉,提交无效组合 - 移动端 iOS Safari 的
<select></select>无法自定义样式或加搜索框,体验割裂
怎么用 fetch 正确加载二级数据
不能把三个接口塞进 Promise.all(),也不能写成同步顺序调用(阻塞 UI)。必须等上一级 value 确实存在,再发起下一级请求。
- 每次
change触发后,立刻执行:citySelect.innerHTML = '<option value="">请选择城市</option>',并设citySelect.disabled = true - URL 必须带上游 ID:
/api/cities?province_id=110000,别拼错参数名 - 必须检查
response.ok,不能只靠.then();404、500、网络中断都得进catch或if (!ok) - 用户快速连点“广东”“江苏”,要用
AbortController中断前一次请求,否则旧响应覆盖新状态
数据结构怎么设计才不翻车
嵌套树形结构(比如 {"广东": {"广州": [...]}})看着直观,但 JS 遍历麻烦、键名不可枚举、类型不一致,实际开发中极易出错。
- 推荐扁平映射表:
const citiesByProvinceCode = { '110000': [{ code: '110100', name: '市辖区' }] } - 查的时候是 O(1):直接
citiesByProvinceCode[provinceSelect.value],不用递归遍历 - 所有
value字段必须是字符串(哪怕后端返回数字,也要String(id)转一下),否则表单提交时可能丢失 - 避免字段名混用:统一小写下划线(
province_code)或驼峰(provinceCode),别一会儿pid一会儿provinceId
回填和键盘导航容易被忽略
编辑已有地址时,直接 citySelect.value = '201' 不会触发 change,也不会展开第三级——因为模拟级联的 JS 逻辑根本没跑。
- 回填必须手动调用初始化函数,并传入完整路径数组,例如
initCascader(['110000', '201', '20101']),逐层展开+选中 - 没加
role="menu"和aria-expanded,屏幕阅读器用户无法感知展开状态 - 没监听
keydown(Tab、ArrowDown),键盘党无法操作,不符合 WCAG 基本要求 - IE11 不支持
replaceChildren(),得用循环移除子节点或降级为innerHTML = ''+ 默认项











