富媒体选择工具(可自定义的 select)

Web 功能: 可自定义的 <select>

过去,原生 <select> 元素很难设置样式,并且只能包含纯文本选项。appearance: base-select 属性提供了一种仅使用 CSS 的声明式方法,可让 <select> 元素进入可自定义状态。这样一来,开发者便可在 <option> 元素中添加丰富的 HTML 内容(例如图片、SVG 和复杂布局),同时保留原生键盘无障碍功能和表单集成。使用此模式,将繁重的自定义选择组件替换为标准的原生元素。

如何实现

如需使用 Customizable Select API 实现富媒体选择工具,请执行以下操作:

  1. 选择启用基本样式:使用 ::picker(select) 伪元素将 appearance: base-select 同时应用于 <select> 元素及其内部选择器。这会更改浏览器针对 <select> 内内容的 HTML 解析器。
  2. 定义按钮内容:在 <select> 中使用标准 <button> 和 <selectedcontent> 元素,定义选择器关闭时显示的内容。<selectedcontent> 元素会自动镜像所选选项的内容。如果您想在按钮中显示当前选择的富媒体内容,则必须执行此操作。
  3. 在选项中使用富媒体内容:您现在可以在 <option> 标记中放置图片、SVG 和其他 HTML 标记。在此 API 推出之前,浏览器会从 <option> 标记中剥离标记,并仅呈现纯文本。
  4. 设置弹出式窗口的样式:通过定位 ::picker(select) 伪元素来设置下拉选项列表的样式。它在顶层渲染,这意味着您无需与 z-index 争夺层级。选项使用 Anchor Positioning API 原生定位。

示例代码:富角色选择器

<label for="role-picker">Select your role</label>
<select class="custom-select" name="role" id="role-picker">
  <button>
    <selectedcontent></selectedcontent> <!-- Mirrors the selected option's content automatically so you do not need JS to update the button -->
  </button>

  <!-- Define concise aria-label values on options whose mirrored rich content would otherwise read awkwardly as a concatenated string -->
  <option value="frontend" aria-label="Frontend Developer">
    <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
      <rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
    </svg>
    <div class="option-text">
      <span class="option-title">Frontend Developer</span>
      <span class="option-desc">React, Vue, CSS</span>
    </div>
  </option>

  <option value="backend" aria-label="Backend Developer">
    <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
      <rect x="2" y="12" width="20" height="14" rx="2" ry="2"></rect>
    </svg>
    <div class="option-text">
      <span class="option-title">Backend Developer</span>
      <span class="option-desc">Node.js, Python</span>
    </div>
  </option>
</select>
select.custom-select,
select.custom-select::picker(select) {
  appearance: base-select; /* MUST opt-in both the select and its picker to enable the customizable state, otherwise browser standard rendering applies */
}

select.custom-select {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 12px;
  background-color: #1e293b;
  border: 1px solid #334155;
  border-radius: 8px;
  color: #f1f5f9;
  cursor: pointer;
}

select.custom-select::picker(select) {
  background-color: #0f172a;
  border: 1px solid #334155;
  border-radius: 8px;
  padding: 8px;
  box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);
  width: anchor-size(width); /* Uses Anchor Positioning API to keep the dropdown precisely aligned to the button trigger width */
}

select.custom-select option {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px;
  border-radius: 4px;
  cursor: pointer;
}

select.custom-select option:hover {
  background-color: #1e293b;
}

/* Remove standard OS checkmark for base-select */
select.custom-select option::before {
  display: none;
}

/* MANDATORY: Provide multiple visual indicators (e.g., prominent background color and bold title font) to communicate the checked state cleanly */
select.custom-select option:checked {
  background-color: #3b82f6;
  color: #ffffff;
}
select.custom-select option:checked .option-title {
  font-weight: 700;
}

策略性实施和最佳实践

  • 建议:当您需要复杂的选项布局(图标、说明、图片)时,请使用 appearance: base-select,以前只有使用繁重的 JavaScript 界面框架才能实现。
  • 如果您发现性能滞后,请不要使用临时元素;浏览器会以原生方式处理原生键盘焦点。
  • 务必考虑顶层渲染。选择器会在顶层呈现,这意味着它会替换页面内容的相对 z-index。
  • 务必通过设置 .custom-select selectedcontent .option-desc { display: none; } 的样式,在按钮状态中隐藏次要细节(例如说明),以免占用过多空间。
  • 测试布局行为。设置 appearance: base-select 会移除默认浏览器行为,即根据最长选项宽度调整选择框大小。您可能需要设置固定宽度或使用 flex/grid 限制来防止布局偏移。
  • 确保您的 <select> 具有 name 属性和关联的 <label>。这样可以确保即使采用自定义界面,屏幕阅读器也能访问该组件,并且该组件能与标准表单提交功能正常配合使用。
  • 在选项中使用标准 value 属性,以确保表单提交功能与之前完全相同。

后备策略

可自定义 <select> 的回退和浏览器支持

对于尚不支持 appearance: base-select 的浏览器,<select> 元素会优雅降级为标准操作系统下拉菜单。

  • 忽略非文本内容:旧版浏览器会剥离 <option> 标记内的 HTML 标记(例如 <svg> 或 <div>),仅呈现文本节点。确保 <option> 的文本内容本身清晰易读且有意义。
  • HTML 结构处理:标准解析器可能会忽略 <select> 内的 <button> 和 <selectedcontent> 标记,或将其视为无效。如果您将标准文本视为可读的后备内容,则渐进增强功能不需要任何繁重的 JavaScript polyfill。
document.addEventListener("DOMContentLoaded", () => {
  // Check if browser supports base-select value
  if (!CSS.supports("appearance", "base-select")) {
    // Custom select overrides are not supported natively.
  }
});