重新探索 HTML select 元素
Rediscover the HTML select element
2026年6月10日
一句话判断
✅ 前端开发者终于可以把那些臃肿的第三方 Select JS 库扔进垃圾桶,用纯 CSS 搞定带图标的下拉菜单,且完全不牺牲屏幕阅读器的体验。
这场 Session 讲了什么
这场 Session 最值得关注的一件事是:Safari 27 联合 Chrome 135 彻底重做了 HTML <select> 元素的样式化能力,推出了 Customizable Select(可定制选择框)。过去十几年,只要设计师敢把下拉菜单画成带图标、带网格布局或者非系统默认样式的,前端就得捏着鼻子引入 Headless UI 或 Select2 这种几十 KB 的 JS 库,用一堆 <div> 模拟行为,还得手动补全 ARIA 标签和键盘导航。
现在规则变了。你只需要给原生 <select> 加上 appearance: base-select,就能用 CSS 随意揉捏按钮和下拉面板。更狠的是,<option> 里终于能塞进 SVG 和图片了,配合 <selectedcontent> 元素,选中项的富文本内容能直接镜像到触发按钮上。对于那些死磕设计还原度又不想被 Lighthouse 无障碍评分教做人的团队来说,这绝对是今年 Web 端最解气的更新。
值得深挖的点
appearance: base-select 背后的妥协与克制
Apple 和 Google 这次没有发明新标签,而是选择给古老的 <select> 动刀,这个设计选择非常务实。如果用新标签(比如 <dropdown>),旧浏览器直接不渲染,Progressive Enhancement(渐进增强)就成了空话。保留 <select> 意味着在不支持的浏览器里,它依然会降级为系统原生弹窗,键盘上下键选择和 VoiceOver 朗读完全不受影响。
但克制也带来了限制。appearance: base-select 只是解除了浏览器的默认样式锁定,暴露了 ::picker(select)(下拉面板)和 ::picker-icon(箭头图标)等伪元素。你没法像写普通 <div> 那样给下拉菜单加复杂的交互动画,也没法在里面嵌套输入框做搜索过滤。如果你需要带搜索功能的 Combo Box,这套原生方案依然不够看,老老实实用 JS 库。它解决的是 90% 纯展示型下拉菜单的痛点,而不是去替代复杂的自动完成组件。
富文本 <option> 与 <selectedcontent> 的魔法
以前 <option> 里只能放纯文本,想加个图标只能靠 Unicode 字符或者背景图 hack。这次 Safari 允许在 <option> 里直接写 <svg> 或 <img>,并且支持用 CSS Grid 把下拉列表排成多列网格。这在摄影师作品集、颜色选择器这种场景下简直是杀手级功能。
最巧妙的设计是 <selectedcontent> 元素。因为原生 <select> 的触发按钮默认只渲染纯文本,当你在 <option> 里放了图标,选中后按钮上却只显示文字,体验非常割裂。把 <selectedcontent> 放在 <select> 内部的第一个 <button> 子元素里,浏览器就会自动把当前选中 <option> 的 DOM 结构克隆到按钮上。这省去了大量监听 change 事件然后手动操作 DOM 的脏活。不过要注意,给 <img> 加 alt="" 是必须的,否则 VoiceOver 会把图片描述和 <option> 文本朗读两遍。
代码片段
1. 基础样式重置与自定义箭头
场景:把系统默认的灰色下拉框改成符合设计系统的圆角按钮,并替换下拉箭头。
/* 开启自定义模式,解除浏览器默认样式 */
select {
appearance: base-select;
background-color: var(--bg-primary);
border: 1px solid var(--border-color);
padding: 0.6em 1em;
border-radius: 8px;
}
/* 替换默认的向下箭头 */
select::picker-icon {
content: url('icons/chevron-down.svg');
width: 16px;
}
/* 菜单展开时改变按钮和箭头状态 */
select:open {
background-color: var(--bg-active);
}
select:open::picker-icon {
content: url('icons/chevron-up.svg');
}
坑:appearance: base-select 必须同时加在 select 和 ::picker(select) 上,否则下拉面板还是会保留系统默认的白底黑边样式。
2. 富文本选项与选中内容镜像
场景:在选项中加入 SVG 图标,并在选中后让图标显示在触发按钮上。
<select>
<!-- 自定义触发按钮,包含 selectedcontent -->
<button>
<selectedcontent></selectedcontent>
</button>
<!-- 选项内直接放置富文本 -->
<option value="portrait">
<svg>...</svg>
<span>人像摄影</span>
</option>
</select>
坑:<button> 必须是 <select> 的第一个子元素。如果 <option> 里有图片,记得写 alt="",防止屏幕阅读器重复朗读。
最佳实践
老项目迁移时,别急着全局替换。先挑那些因为样式问题被设计师抱怨最多、但又没加搜索功能的简单 <select> 进行改造。把 div 模拟的假下拉菜单换回原生 <select>,能直接砍掉几百 KB 的 JS 依赖。
新项目直接拥抱 <selectedcontent> 和 Grid 布局下拉面板。但在写 CSS 时,务必在 Safari Technology Preview 和 Chrome Canary 里交叉测试 ::picker(select) 的层叠上下文(Stacking Context),目前早期版本在处理带 box-shadow 的下拉面板遮挡问题时偶尔会有 bug。
永远记住,这套方案的底线是 Accessibility(无障碍)。不要为了炫酷的动画去拦截键盘事件,把默认的上下键导航和回车选中逻辑留给浏览器,你只负责画皮。
还有什么值得关注
- Grid 布局下拉菜单:
::picker(select)支持display: grid,可以直接把长列表变成多列网格,做分类选择器时极其好用。 ::checkmark伪元素:选中项前面的默认打勾图标现在可以通过 CSScontent属性随意替换成自定义 SVG。- 跨浏览器一致性:Safari 27 和 Chrome 135 同步首发,这意味着 WebKit 和 Blink 在表单控件自定义上达成了罕见的共识,不用写两套前缀代码。