选完了,可 select 按钮里还是只有一行冷淡的纯文本——把 selectedcontent 放进 button,选中的图标和标签就能出现在关闭态里
用户点开下拉、选了「带图标的建筑」那一项,菜单关上之后,按钮里却只剩一行冷冰冰的纯文本「建筑」。你想要的是一张缩略图加一行标签,但原生 select 的关闭态根本不给你这个位置——这十几年要么忍,要么整块换成 div + JS 重写,然后把键盘导航和读屏支持自己重新实现一遍。
Safari 27 的可定制 select 把这个缺口补上了,而且补得很巧:你现在可以合法地把一个 <button> 作为 <select> 的第一个子元素(以前这在 HTML 里是禁止的),在它里面放一个全新的 <selectedcontent> 元素。这个元素会自动镜像当前选中 <option> 里的富内容——图标、标签、缩略图——让它们在按钮关闭状态下原样显示。
先说结论:<selectedcontent> 不是又一个装饰性伪元素,它解决的是「富菜单能开、却收不回按钮里」这个一直悬着的最后一公里。而且它是渐进增强的——不支持的浏览器自动回退到原生弹窗,语义化 <select> 的无障碍能力一处都不丢。
它凭什么能镜像内容
<selectedcontent> 的机制是「克隆」。当选中项变化时,浏览器会把新选中 <option> 的 DOM 内容克隆一份,塞进这个元素里。所以你在 option 里放的东西——内联 SVG、<img>、一段带 class 的文本——都会原封不动出现在按钮里。
标准写法是这样的:
<select>
<button>
<selectedcontent></selectedcontent>
</button>
<option value="buildings">
<img src="icons/buildings.svg" alt="">
<span>建筑</span>
</option>
</select>
配套的 CSS 只需要两处 appearance: base-select,一处开按钮样式,一处退出原生下拉:
select,
::picker(select) {
appearance: base-select;
}
select > button,
selectedcontent,
option {
display: flex;
align-items: center;
gap: 10px;
}
option::checkmark { display: none; }
三个必须知道的坑
第一,按钮里的内容是 inert(惰性) 的。克隆进来的控件不能聚焦、不能点,它纯粹是展示。别指望在选中态里塞一个可交互的开关。
第二,克隆不会自动刷新。你改动了当前选中 option 的子内容,现有克隆不一定跟着更新,直到用户重新选择一次。这在 React/Vue 这种数据驱动里要特别小心——用框架直接改 option 内容,按钮里的镜像可能是旧的。
第三,读屏重复播报。Apple 的 WWDC26 session 315 特意提醒:option 里带图标时,把 <img> 的 alt 留空,让读屏只念一次文字标签,否则会出现「图片描述 + 文本」念两遍。
为什么值得迁移,而不是继续用 div
对比一下老方案。以前要做这个效果,你得:隐藏原生 select、用 div 拼一个假的按钮、监听 change 事件、用 JS 把选中项的 innerHTML 手动搬进按钮、再补 role=”combobox”、aria-expanded、键盘上下键、Esc 关闭、读屏状态……一个下拉菜单库动辄几十 KB,而且无障碍还得反复测。
现在这套是:一个语义化 <select> + 两个 appearance + 一个 <selectedcontent>。键盘导航、Esc、读屏、表单提交,全部由浏览器原生负责。少了一个依赖,还多了一层稳。
什么时候能用
可定制 select 从 Safari 27 和 Chrome 135 起提供(Firefox 和 Safari 的 <selectedcontent> 完整支持仍在推进中,以 caniuse 实测为准)。所以在生产环境里,请把它当成渐进增强:支持它的用户看到带图标的按钮,不支持的用户看到完整的原生下拉——因为没有一处离开语义化 select,降级是免费的。
可落地的下一步
- 挑一个「选项本身带视觉信息」的下拉——货币、语言、分类、团队头像——作为试点。
- 先只加
appearance: base-select和::picker-icon,把按钮和箭头的样式跑通。 - 再把
<selectedcontent>放进去,让选中项的图标和标签回到关闭态。 - 最后拿 VoiceOver 和一台不支持该特性的浏览器各测一遍,确认降级路径可用。
一句话:<selectedcontent> 让「富到菜单里、空在按钮上」这个长期割裂终于合拢了。保留 <select> 的语义,把样式主权拿回来。
评论区
登录后可评论。