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 负责。