真实项目中三级地址联动应使用div+ul+JS模拟下拉,而非原生select;需懒加载数据、维护selectedPath路径状态、用touchstart监听收起弹层,并通过china-division库或areas.json获取准确行政区划数据。

用 select 做三级地址联动,但别真用原生 select
原生 <select></select> 在地址选择场景下几乎不可用:不支持搜索、无法展开多级、移动端体验差、样式难统一。真实项目里,99% 的“地址选择器”都是用 div + ul + JavaScript 模拟的下拉行为,配合地区数据(省市区三级 JSON)动态渲染。
关键不是“怎么画个下拉框”,而是“怎么让三级联动不卡顿、不漏数据、用户点两下就能选完”。常见错误是把全部 3000+ 行政区一次性塞进 DOM,导致点击延迟或内存暴涨。
- 优先加载省级列表,用户点开某省后,再异步请求该省下的市级数据(可预置在前端 JSON 中,避免发请求)
- 区/县级数据建议懒加载:只在用户展开市时才渲染对应区县,而非一次性全量挂载
- 用
dataset存储每个li的code(如110000)和level("province"/"city"/"district"),方便后续取值
省市数据从哪来?别手敲,也别硬写死在 JS 里
中国行政区划会更新(比如 2023 年新增了东莞为副省级市、部分县改区),手写 JSON 或直接贴进代码等于埋雷。推荐两种方式:
- 用现成轻量库:
china-division(npm 包,仅 12KB),导出结构清晰的嵌套数组,支持 ESM / CommonJS - 自己维护一个
areas.json文件放在/public下,通过fetch('/areas.json')加载 —— 适合不想引入依赖、且能接受手动同步更新的项目
注意:别用网上搜到的“最新行政区划 Excel 转 JSON”工具生成的数据,很多漏掉“市辖区”或把“林芝市巴宜区”错标为“林芝地区巴宜县”。校验重点看:西藏、新疆、内蒙古的地级单位是否含“地区”“盟”“自治州”,以及直辖市下是否直接有“区”而无“市”。
立即学习“前端免费学习笔记(深入)”;
onChange 拿不到完整地址?因为没存路径,只存了当前级
典型翻车现场:用户选了“广东省 → 深圳市 → 南山区”,但 onChange 回调里只有 { code: '440305', name: '南山区' },没留“广东省”和“深圳市”。这不是事件设计问题,是你没维护选中路径状态。
- 用一个数组
selectedPath记录每级选中的对象,例如[{code:'440000',name:'广东省'}, {code:'440300',name:'深圳市'}, {code:'440305',name:'南山区'}] - 每次切换某一级时,截断该级之后所有项(如从深圳切到广州,则清空之前选的“南山区”),再 push 新项
- 最终提交时,直接取
selectedPath.map(i => i.name).join(' ')得到“广东省 深圳市 南山区”
别依赖 DOM 上的文本拼接(比如读三个 span 的 innerText),DOM 可能未及时更新,或被用户手动改过。
移动端点击空白收不回弹层?检查 touchstart 和 focus 冲突
在 iOS Safari 或微信 WebView 里,地址选择器点空白处不关闭,大概率是因为用了 click 监听 document,但移动端 click 有 300ms 延迟,且焦点切换时可能被 input 自动聚焦打断。更稳的方式是:
- 监听
touchstart(非click)事件,在事件捕获阶段判断点击目标是否在弹层外 - 给地址触发按钮加
tabindex="0",并监听blur—— 当用户用键盘 Tab 离开时也收起弹层 - 禁止 body 滚动:弹层打开时加
body { position: fixed; width: 100%; },否则 iOS 滚动会穿透到背景页
最常被忽略的是:安卓某些定制浏览器(如华为浏览器)对 position: fixed 支持异常,此时得 fallback 到 body { overflow: hidden; } + 手动记录 scrollTop。



















