直接用select嵌套联动会出问题,因为浏览器原生select不支持动态禁用/清空子级而不触发重绘抖动,且无法预设“市”为空状态,易导致选中错位、脏数据提交。

为什么直接用 select 嵌套联动会出问题
因为浏览器原生 select 不支持动态禁用/清空子级选项而不触发重绘抖动,且无法在用户未操作时预设「市」为空状态——一旦「省」变更,「市」select 的 selectedIndex 仍为上一次值,容易提交脏数据。
常见错误现象:change 事件里清空下级 select 后立即 appendChild 新 option,但 DOM 渲染顺序不可控,导致选项闪动或选中错位。
- 必须先清空
innerHTML或调用removeChild,再批量插入,避免反复重排 - 「区」级
select初始应设disabled属性,等「市」选中后再启用 - 不要用
innerHTML +=拼接选项,会触发整个节点重渲染
用 JSON 数据 + dataset 实现轻量级省市区映射
别从后端实时拉取三级数据,先加载一份精简的 province-city-district.json(约 200KB),用 dataset 把城市列表挂到省级 option 上,查得快、不发请求。
示例结构:
立即学习“前端免费学习笔记(深入)”;
{
"110000": {
"name": "北京市",
"cities": {
"110100": {
"name": "市辖区",
"districts": ["110101", "110102", "..."]
}
}
}
}
关键操作:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 省级
select的每个option加data-code="110000" - 监听省级
change,读取event.target.selectedOptions[0].dataset.code - 用该 code 查 JSON 获取对应 cities 对象,遍历生成市级
option - 市级
select的option同样带data-code,用于查区
disabled 和 required 的联动控制逻辑
用户跳过「省」直接点「提交」,表单校验会失败;但若只给「省」加 required,「市」「区」仍可为空提交。必须手动同步校验状态。
实操建议:
- 三个
select都加required,但初始时仅「省」可操作,「市」「区」加disabled - 「省」选中后,移除「市」的
disabled并聚焦它;「市」选中后同理处理「区」 - 任一级切换为空(比如选了「省」又改回默认项),下游所有
select清空 +disabled+ 移除value - 提交前检查
select[value=""]是否存在,有则阻止提交并提示「请完整选择省市区」
移动端 Safari 下 select 的滚动与 focus 异常
Safari 对 select 的原生弹出层控制极弱:无法滚动到顶部、无法通过 JS 触发弹出、focus() 无反应。强行 hack 容易触发页面缩放或键盘误弹。
更稳妥的做法:
- 检测
navigator.userAgent.includes("Safari") && !navigator.userAgent.includes("Chrome") - 对 iOS 设备,把三级选择器换成
div+scrollable滚动区域,用aria-haspopup和role="listbox"保无障碍 - 点击「省」触发全屏弹层,内部用
ul/li模拟选项,选中后关闭并同步到隐藏的select(供表单提交用) - 不要尝试用
-webkit-appearance: none改样式,iOS 下会丢失原生滚动优化
复杂点在于:同一份数据要同时适配原生 select 和自定义弹层,JSON 结构必须扁平化、带层级标识,且两级联动不能靠 DOM 关系推导,得靠 code 字段精确匹配。


















