HOWTO · CSS

CSS 中的样式选择下拉菜单

本教程介绍了如何在 CSS 中设置下拉菜单的样式。

本页内容

可以通过调整原生 HTML select 下拉控件的外观,并用 CSS 绘制的箭头替换默认箭头来设置样式。它仍是真正的 select,因此用户可以继续使用内置的选项选择行为和键盘交互。常见做法有两种:直接移除原生外观,或用包装元素裁切掉原生箭头。

为原生 select 下拉控件设置样式,同时保留其行为

select 包含 option 元素,浏览器会按照所在平台和浏览器的惯例来呈现控件。CSS 可以改变可见控件框的样式,但弹出选项列表及其他一些细节仍会因浏览器和操作系统而异。如果需要保留表单和键盘操作的内置行为,请继续使用原生元素,而不要用通用元素重新创建下拉控件。

示例使用相同的四个选项。CSS 为右侧箭头留出了空间,并为键盘用户提供清晰可见的焦点轮廓。你可以调整颜色和尺寸,使其符合界面设计。

使用 appearance: none 隐藏原生箭头并添加自定义箭头

在 select 上设置 appearance: none,即可在支持此属性的浏览器中移除原生箭头;然后用两个 CSS 渐变绘制一个 V 形箭头。background 的图层会将两条线放在控件右侧附近;右侧内边距则避免选项文字与箭头重叠。-webkit-appearance 和 -moz-appearance 是供使用这些前缀的引擎识别的厂商前缀形式;它们并不分别对应独立的 WebKit 或 Blink 浏览器系列。标准属性是主要声明,只有在目标浏览器支持要求需要时才保留前缀。

这是一个完整的静态浏览器示例。将其保存为 HTML 文件并在浏览器中打开,即可查看控件;数据包没有提供浏览器渲染证据,因此这里不声称已观察到具体的视觉结果。

本节中的代码以及显示的标签或输出仍为英文。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Styled select</title>
  <style>
    .vehicle-select {
      box-sizing: border-box;
      width: 220px;
      height: 40px;
      padding: 0 32px 0 10px;
      border: 1px solid #777;
      border-radius: 4px;
      color: #222;
      background-color: #fff;
      background-image:
        linear-gradient(45deg, transparent 50%, #333 50%),
        linear-gradient(135deg, #333 50%, transparent 50%);
      background-position:
        calc(100% - 14px) 50%,
        calc(100% - 9px) 50%;
      background-size: 5px 5px, 5px 5px;
      background-repeat: no-repeat;
      appearance: none;
    }

    .vehicle-select:focus-visible {
      outline: 3px solid #1769aa;
      outline-offset: 2px;
    }
  </style>
</head>
<body>
  <label for="vehicle">Choose a vehicle</label>
  <select class="vehicle-select" id="vehicle" name="vehicle">
    <option>RE Himalayan</option>
    <option selected>CRF Rally</option>
    <option>GS 310</option>
    <option>KTM ADV</option>
  </select>
</body>
</html>

使用裁切包装元素隐藏原生箭头

包装元素也可以用来隐藏原生箭头。将 select 的宽度设为略大于包装元素,并把包装元素的 overflow 设为 hidden;这样原生箭头就会落在包装元素的可见范围之外。再用伪元素在包装元素上绘制替代箭头。由于 select 仍位于包装元素内,其选项和原生交互功能都可以继续使用。

包装元素的宽度应足以容纳控件文字,而 select 只需稍微宽一些。如果宽度差太大,控件的右侧边框或其他内容也可能被裁切。伪元素使用 pointer-events: none,因此不会拦截对 select 的点击。

在包装元素示例中,select 比包装元素宽 20 像素。下面这段 JavaScript 检查明确验证了这一宽度关系:

本节中的代码以及显示的标签或输出仍为英文。

const wrapperWidth = 200;
const selectWidth = 220;
if (selectWidth <= wrapperWidth) {
  process.exitCode = 1;
}

当 select 比包装元素宽时,检查会成功完成;否则会设置失败退出码。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Select with clipped arrow</title>
  <style>
    .select-wrap {
      position: relative;
      box-sizing: border-box;
      width: 200px;
      height: 42px;
      overflow: hidden;
      border: 1px solid #777;
      border-radius: 4px;
      background: #fff;
    }

    .select-wrap select {
      box-sizing: border-box;
      width: 220px;
      height: 40px;
      padding: 0 10px;
      border: 0;
      color: #222;
      background: transparent;
      font: inherit;
    }

    .select-wrap::after {
      position: absolute;
      top: 18px;
      right: 12px;
      width: 7px;
      height: 7px;
      border-right: 2px solid #333;
      border-bottom: 2px solid #333;
      content: "";
      pointer-events: none;
      transform: rotate(45deg);
    }

    .select-wrap:focus-within {
      outline: 3px solid #1769aa;
      outline-offset: 2px;
    }
  </style>
</head>
<body>
  <label for="vehicle">Choose a vehicle</label>
  <div class="select-wrap">
    <select id="vehicle" name="vehicle">
      <option>RE Himalayan</option>
      <option selected>CRF Rally</option>
      <option>GS 310</option>
      <option>KTM ADV</option>
    </select>
  </div>
</body>
</html>

选择一种做法并检查其限制

如果你希望由 select 自身决定宽度和边框,而且目标浏览器支持所需样式,可以使用 appearance: none。如果需要在包装元素上放置箭头,或不适合直接设置外观样式,可以使用裁切包装元素。无论采用哪种做法,都应在目标浏览器和设备上测试页面,因为原生 select 的呈现方式和弹出菜单并不完全一致。

检查所选值是否完整可见、自定义箭头是否对齐,以及键盘焦点是否清晰可见。如果包装版本中的边框或文字看起来被截断,请对照检查包装元素和 select 的宽度。不要为了让控件看起来更简洁而移除焦点指示器;如果要自定义样式,应换成同样清晰的焦点样式。这些做法只改变呈现方式,选项选择和表单行为仍由原生 select 负责。