选完了,可 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,降级是免费的。

可落地的下一步

  1. 挑一个「选项本身带视觉信息」的下拉——货币、语言、分类、团队头像——作为试点。
  2. 先只加 appearance: base-select::picker-icon,把按钮和箭头的样式跑通。
  3. 再把 <selectedcontent> 放进去,让选中项的图标和标签回到关闭态。
  4. 最后拿 VoiceOver 和一台不支持该特性的浏览器各测一遍,确认降级路径可用。

一句话:<selectedcontent> 让「富到菜单里、空在按钮上」这个长期割裂终于合拢了。保留 <select> 的语义,把样式主权拿回来。

评论区

0 条评论

登录后可评论。