移动端筛选面板不能直接用select元素,因其在ios/android会触发系统级下拉菜单,脱离样式控制、无法嵌入滚动区、不支持多选/联动、聚焦收起异常;故需手写dom+css+js实现。

移动端筛选面板为什么不能直接用 select 元素
因为原生 select 在 iOS 和部分 Android 上会触发系统级下拉菜单,完全脱离页面样式控制,无法嵌入滚动区域、无法自定义多选/联动逻辑、无法响应点击位置(比如点“取消”要关闭面板但不提交)。更关键的是:它无法在固定定位的弹层里正常聚焦和收起——用户点完选项后面板常卡住,或点空白处不关闭。
所以真实项目中,99% 的移动端筛选面板都是手写 DOM + CSS + JS 控制显隐与交互。
- 用
div模拟选项容器,配合data-value存储实际值 - 用
position: fixed或position: absolute实现遮罩层+面板组合 - 禁止
body滚动(document.body.style.overflow = 'hidden'),否则背景页会滑动 - 点击遮罩层(非选项区)必须触发关闭,且需防 touchmove 误判
如何实现「多级联动筛选」(比如省→市→区)
核心是状态隔离 + 异步加载边界处理。不能把所有数据一次性塞进 DOM,尤其三级以上时内存和渲染都吃紧。
推荐结构:data-level="1" 标记当前展开层级,每个筛选项带 data-parent-id,点击后清空下级容器并请求新数据(或从预加载 JSON 中过滤)。
- 首次展开一级时,只渲染省级列表;选中某省后,才发请求拉取该省下的市级数据
- 若使用本地 JSON,用
Array.filter()比遍历 DOM 更快,避免重复 querySelector - 每次切换上级时,必须重置下级的
data-selected状态,否则残留选中态会导致提交错乱 - 注意「返回」按钮逻辑:不是 history.back(),而是
level--并还原上一级 DOM 状态
scrollIntoView({ block: 'nearest' }) 在筛选面板里的实际作用
当筛选项过多、面板内部可滚动时,用户点击某个选项后,常希望它自动居中或至少可见。但直接用 scrollIntoView() 默认行为在移动端容易滚动过猛、触发 body 回弹,甚至让键盘意外弹出。
关键参数必须显式设置:
-
{ block: 'nearest', inline: 'nearest' }:避免强制滚动到顶部/底部,适配小屏 - 调用前加节流(
setTimeout延迟 16ms),防止连点导致多次滚动冲突 - 仅对当前激活面板内的元素调用,别误操作其他已隐藏的
div - 如果用了
transform: translateY()做动画入场,需等 transition 结束再 scroll,否则位置计算错误
提交筛选结果时,为什么 FormData 不适用
因为筛选面板往往包含「未激活项」(比如未点开的二级菜单)、「多选组合」(如多个标签)、以及「范围选择」(如价格区间)。FormData 只适合表单控件(input/select/textarea)的键值对提交,而筛选面板的 DOM 是纯语义化结构,没有 name 属性驱动机制。
正确做法是手动收集状态:
- 用对象记录各维度选中值:
{ province: 'zj', city: ['hz', 'nb'], tags: ['hot', 'new'] } - 多选用
Array.from(document.querySelectorAll('.filter-item.active')).map(el => el.dataset.value) - 范围类(如价格)取
input[type=range]的value,或两个input[type=number]的min/max值 - 提交前做空值过滤(比如用户没选任何城市,就不要传
city: [],后端可能报错)
最易被忽略的是「取消筛选」按钮:它不该只是清空 UI,而要恢复初始默认值(比如时间范围回到「最近7天」,而非全空),否则用户二次打开面板时状态丢失。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











