原生 不能作为下拉式多选使用,因其按规范必须以垂直滚动列表形式呈现;需自定义组件实现点击展开、勾选、收起,并确保可访问性与移动端兼容。

原生 <select multiple></select> 不能当“下拉式多选”用,加了 multiple 就会变成垂直列表,这不是 bug,是规范行为;真要点击展开、勾选、再收起,必须用自定义组件。
为什么 <select multiple></select> 默认不显示为下拉
浏览器按 HTML 规范渲染:multiple 的语义是“同时展示多个可选项”,所以它强制以滚动列表形式呈现,而非折叠状态。用户点一下就全展开,没有“下拉动画”或“点击触发”的交互逻辑。
- 加了
multiple却还期待点击箭头展开?那是误解——原生控件根本不支持这个行为 -
size属性只是控制可见行数(比如size="3"显示 3 行),不是“模拟下拉高度”,对 UX 提升有限 - iOS Safari 对
<select multiple></select>支持极差,多数情况下点开只响应单选,甚至直接调用系统单选 Picker
如何用 <select multiple></select> 正确提交数据
它能用,但必须绕过表单直传的惯性思维,尤其注意前后端衔接点:
- 必须带
name属性,否则 POST/GET 不传值 ——<select name="tags" multiple></select> - 后端接收时,
tags是字符串数组:Python 用request.form.getlist('tags'),PHP 用$_POST['tags'](自动为 array) - JS 获取选中项,别遍历
options手动查selected,直接用selectElement.selectedOptions,它返回的是实时的HTMLCollection - 若需同步到隐藏
<select></select>(比如兼容旧逻辑),记得每次变更后手动设置option.selected = true/false,并触发change事件
移动端和可访问性绕不过的坑
想用纯 CSS + JS 模拟下拉多选?以下三点漏一个,就等于没做完整:
- 隐藏原生
<select multiple></select>时,别用display: none或visibility: hidden,改用position: absolute; left: -9999px,否则屏幕阅读器会跳过 - 自定义容器必须加
role="listbox",每个选项加role="option"和aria-selected,且 JS 必须动态同步aria-expanded状态 - 键盘操作必须支持:Tab 进入、方向键切换焦点、空格/Enter 切换选中、Esc 收起 —— 多数 DIY 实现只处理鼠标,残障用户完全无法使用
React/Vue 中用现成库的硬约束
用 react-select 或 vue-multiselect 看似省事,但受控模式下几个细节极易翻车:
- React:传给
value的必须是数组,onChange回调接收的也是新数组(不是event对象),写成onChange={(v) => setValue(v)}才对 - Vue:绑定的数组必须是响应式新引用,
v-model直接.push()或.splice()原数组,视图不会更新;要用ref包裹或computed返回新数组 - 所有库都依赖
key稳定性:选项对象的key别用Math.random()或数组索引,否则多选后排序、搜索、过滤时 UI 错乱
真正难的从来不是“怎么让多选看起来像下拉”,而是让这个交互在 iOS 上可用、在键盘上可操作、在屏幕阅读器里可理解——这些细节堆起来,才是生产环境里逃不掉的复杂点。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











