原生无法满足设计需求,因其在Chrome/Firefox中CSS样式受限,禁止嵌套HTML,无法实现图标、多行文本或自定义颜色等视觉效果。

为什么原生 <select></select> 无法满足设计需求
原生 <select></select> 在 Chrome/Firefox 中样式高度受限,background、padding、border-radius 等 CSS 属性在部分浏览器(尤其是 Windows 下的旧版 IE/Edge)中会被忽略;更关键的是,<option></option> 标签**不允许嵌套 HTML 元素**,无法插入图标、多行文本或自定义颜色。如果你看到下拉项里有 Emoji 或状态徽标,那一定不是靠原生 <option></option> 实现的。
用 <div> + <code><button></button> + <ul></ul> 模拟下拉结构
核心思路是隐藏原生 <select></select>,用语义化但可完全控制的标签组合替代视觉层:
-
<button></button>作为触发器,显示当前选中值(支持任意 HTML 内容) -
<ul></ul>作为下拉列表容器,position: absolute定位,z-index需高于页面其他元素 -
<li>作为每个选项,绑定click事件并更新按钮文本和隐藏的<select></select>值 - 必须监听
click外部区域(document)来关闭菜单,否则点击空白处不收起
示例关键结构:
<div class="custom-select">
<button aria-haspopup="true" aria-expanded="false">请选择</button>
<ul role="listbox" aria-hidden="true">
<li role="option" data-value="1">✅ 已验证邮箱</li>
<li role="option" data-value="2">⚠️ 待审核</li>
</ul>
<select style="position: absolute; opacity: 0;" name="status">
<option value="1">已验证邮箱</option>
<option value="2">待审核</option>
</select>
</div>
aria-* 属性不是装饰,而是可访问性刚需
去掉 ARIA 属性后,屏幕阅读器会把整个 <div> 当作普通容器,用户无法感知这是下拉菜单,也无法用键盘导航选项。必须设置:
<ul>
<li>
<code>aria-haspopup="true" 和 aria-expanded 动态切换(true/false),告知展开状态
role="listbox" 绑定在 <ul></ul> 上,role="option" 绑定在每个 <li> 上aria-hidden="true" 初始设为 true,展开时改为 false;同时配合 tabindex="-1" 控制键盘焦点流Enter 或 Space 键应触发展开/收起,ArrowDown/ArrowUp 应在选项间移动焦点移动端点击穿透与 focus 状态丢失问题
iOS Safari 和部分 Android 浏览器存在“假点击”:点击 <button></button> 后立即展开 <ul></ul>,但紧接着又触发了 <li> 的 click,导致刚展开就选中第一项。解决方法:
- 给
<button></button>添加touchstart事件并preventDefault(),延迟 100ms 再展开,避开合成点击 - 不要依赖
:focus样式做高亮,iOS 上button:focus可能不触发;改用 JS 切换aria-selected和自定义 class - 收起菜单时,确保
<button></button>重新获得焦点(button.focus()),否则键盘用户无法继续操作
真实项目中,这类交互的边界情况比想象中多:快速连点、从键盘 tab 进入、屏幕阅读器切换模式……别只测鼠标点击。

















