1. 为什么 <select> 的滚动条很难自定义?
  2. 原生 <select> 的滚动条在不同浏览器中是什么样?
  3. 如何隐藏原生 <select> 的滚动条?
  4. 如何创建一个完全自定义样式的“下拉列表”(推荐方案)
  5. 使用现成的UI库(最快方案)

为什么 <select> 的滚动条很难自定义?

这是最核心的问题。<select> 元素是一个“原生控件”(Native Widget)

网页select下拉列表滚动条
(图片来源网络,侵删)
  • 由操作系统渲染:这意味着它的外观和行为是由你当前操作系统(Windows, macOS, Linux)和浏览器(Chrome, Firefox, Edge, Safari)共同决定的,而不是由网页的 CSS 控制。
  • 安全限制:出于安全和一致性的考虑,浏览器不允许开发者通过 CSS 深度修改原生控件的核心样式,比如滚动条、边框的圆角、下拉箭头的形状等,你可以改变一些基本属性(如 border, padding, font-size),但无法触及其“灵魂”。

不要试图用 CSS 去深度美化 <select> 本身的滚动条,这条路走不通。


原生 <select> 的滚动条在不同浏览器中是什么样?

了解这一点有助于你理解为什么需要自定义方案。

浏览器 滚动条外观 特点
Chrome / Edge 窄、简洁,滚动条轨道(track)和滑块(thumb)颜色很淡。 默认样式比较现代,但宽度很窄,选项多时不易拖动。
Firefox 宽度比 Chrome 稍宽,样式更传统。 轨道和滑块有明显的背景色和边框。
Safari 风格与 macOS 系统保持一致,非常简洁。 滑块是圆角矩形,轨道几乎透明。

可以看到,不同浏览器下样式差异巨大,这会给你的网页设计带来不一致的体验。


如何隐藏原生 <select> 的滚动条?

虽然不能美化,但我们可以隐藏它,这通常是在自定义下拉列表方案中需要的一步,目的是用我们自己的滚动条(或滚动逻辑)来替代。

隐藏滚动条的方法取决于你的需求。

隐藏整个滚动条(适用于所有浏览器)

这个方法会完全隐藏滚动条,用户只能通过鼠标滚轮或键盘上下键来滚动。

.select-wrapper select {
  /* 隐藏滚动条,但保持功能 */
  -ms-overflow-style: none;  /* IE and Edge */
  scrollbar-width: none;  /* Firefox */
}
.select-wrapper select::-webkit-scrollbar {
  /* Chrome, Safari and Opera */
  display: none;
}

只隐藏滑块,保留轨道(视觉效果更好)

这个方法只隐藏滚动条的滑块部分,但保留轨道的背景,视觉上更完整,但请注意,此方法在 Firefox 中不完全支持

.select-wrapper select {
  /* 隐藏滑块,但保留轨道(Webkit内核浏览器) */
  -webkit-appearance: none; /* 移除默认样式,是生效的前提 */
}
.select-wrapper select::-webkit-scrollbar {
  width: 8px; /* 设置轨道宽度 */
}
.select-wrapper select::-webkit-scrollbar-track {
  background: #f1f1f1; /* 轨道背景色 */
  border-radius: 4px;
}
.select-wrapper select::-webkit-scrollbar-thumb {
  -webkit-appearance: none; /* 关键:隐藏滑块 */
  background: transparent; /* 将滑块设为透明 */
}
/* 对于 Firefox,只能隐藏整个滚动条 */
.select-wrapper select {
  scrollbar-width: none; 
}

如何创建一个完全自定义样式的“下拉列表”(推荐方案)

既然无法美化原生 <select>,我们就用 HTML 和 CSS 重新“造一个”,这是目前最主流、效果最好的方法。

核心思路

  1. 用一个 <div> 作为下拉列表的容器。
  2. 用一个 <span><div> 显示当前选中的值。
  3. 用一个 <ul> 作为选项列表,并默认隐藏它。
  4. 用 CSS 美化这些元素,包括自定义滚动条。
  5. 用 JavaScript 处理点击、选择、滚动等交互逻辑。

示例代码

下面是一个完整的、可运行的例子,包含了自定义滚动条。

HTML 结构

<div class="custom-select">
  <div class="select-selected">
    请选择一个城市
    <span class="select-arrow">▼</span>
  </div>
  <ul class="select-items">
    <li data-value="beijing">北京</li>
    <li data-value="shanghai">上海</li>
    <li data-value="guangzhou">广州</li>
    <li data-value="shenzhen">深圳</li>
    <li data-value="chengdu">成都</li>
    <li data-value="hangzhou">杭州</li>
    <li data-value="wuhan">武汉</li>
    <li data-value="xian">西安</li>
    <li data-value="chongqing">重庆</li>
    <li data-value="nanjing">南京</li>
    <li data-value="suzhou">苏州</li>
    <li data-value="tianjin">天津</li>
  </ul>
</div>
<!-- 同时保留一个原生 select 供表单提交使用,并隐藏它 -->
<select name="city" id="city-select" style="display: none;">
  <option value="">请选择一个城市</option>
  <option value="beijing">北京</option>
  <option value="shanghai">上海</option>
  <option value="guangzhou">广州</option>
  <option value="shenzhen">深圳</option>
  <option value="chengdu">成都</option>
  <option value="hangzhou">杭州</option>
  <option value="wuhan">武汉</option>
  <option value="xian">西安</option>
  <option value="chongqing">重庆</option>
  <option value="nanjing">南京</option>
  <option value="suzhou">苏州</option>
  <option value="tianjin">天津</option>
</select>

CSS 样式

body {
  font-family: Arial, sans-serif;
  padding: 40px;
}
.custom-select {
  position: relative;
  width: 200px;
  font-size: 16px;
  user-select: none; /* 防止用户选中文本 */
}
.select-selected {
  background-color: #f8f9fa;
  border: 1px solid #ced4da;
  border-radius: 5px;
  padding: 10px 15px;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.select-selected:hover {
  background-color: #e9ecef;
}
.select-arrow {
  transition: transform 0.3s ease;
}
.select-items {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background-color: #fff;
  border: 1px solid #ced4da;
  border-top: none;
  border-radius: 0 0 5px 5px;
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 150px; /* 限制最大高度 */
  overflow-y: auto; /* 启用垂直滚动 */
  z-index: 10;
  display: none; /* 默认隐藏 */
}
/* 自定义滚动条样式 */
.select-items::-webkit-scrollbar {
  width: 8px;
}
.select-items::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 4px;
}
.select-items::-webkit-scrollbar-thumb {
  background: #888;
  border-radius: 4px;
}
.select-items::-webkit-scrollbar-thumb:hover {
  background: #555;
}
.select-items li {
  padding: 10px 15px;
  cursor: pointer;
}
.select-items li:hover {
  background-color: #f8f9fa;
}
/* 当下拉列表打开时,改变箭头方向 */
.custom-select.open .select-arrow {
  transform: rotate(180deg);
}
/* 当下拉列表打开时,显示选项列表 */
.custom-select.open .select-items {
  display: block;
}

JavaScript 交互逻辑

document.addEventListener('DOMContentLoaded', function() {
  const customSelect = document.querySelector('.custom-select');
  const selectedDiv = customSelect.querySelector('.select-selected');
  const itemsList = customSelect.querySelector('.select-items');
  const hiddenSelect = document.getElementById('city-select');
  // 1. 点击选中区域,打开/关闭下拉列表
  selectedDiv.addEventListener('click', function() {
    customSelect.classList.toggle('open');
  });
  // 2. 点击选项
  itemsList.addEventListener('click', function(e) {
    if (e.target.tagName === 'LI') {
      // 更新显示的文本
      selectedDiv.innerHTML = `${e.target.textContent} <span class="select-arrow">▼</span>`;
      // 更新原生 select 的值,以便表单提交
      hiddenSelect.value = e.target.getAttribute('data-value');
      // 关闭下拉列表
      customSelect.classList.remove('open');
    }
  });
  // 3. 点击页面其他地方,关闭下拉列表
  document.addEventListener('click', function(e) {
    if (!customSelect.contains(e.target)) {
      customSelect.classList.remove('open');
    }
  });
});

使用现成的 UI 库(最快方案)

如果你不想自己动手,或者项目已经使用了某个 UI 库,直接使用它们提供的组件是最快、最稳定的选择,这些库已经完美解决了兼容性和样式问题。

  • Ant Design (React)

    • 组件:Select
    • 特点:样式精美,功能强大(支持搜索、多选、远程加载等),有完整的自定义滚动条样式。
  • Element Plus (Vue)

    • 组件:Select 选择器
    • 特点:Vue 生态中非常流行,文档完善,可定制性高。
  • Bootstrap

    • 组件:Custom Selects
    • 特点:提供了基础的自定义样式,但滚动条仍然是原生的,对于更高级的定制,可能需要结合 JavaScript 或其他库。
  • Headless UI (无头 UI 库)

    这是一个更底层的库,它只处理状态和交互逻辑,不提供任何样式,你可以完全自由地用 CSS 去装饰它,非常适合追求极致自定义的开发者。

方案 优点 缺点 适用场景
原生 <select> - 代码简单
- 无需 JS
- 无障碍性最好
- 样式难以控制
- 浏览器差异大
- 体验受限
对样式要求极低,或需要快速实现的内部工具。
自定义下拉列表 - 样式完全可控
- 体验一致性好
- 可添加动画等高级效果
- 需要编写 HTML, CSS, JS
- 需要考虑无障碍性
- 代码量稍多
大多数商业项目、官网、对 UI 有要求的场景。
UI 库 - 开发速度快
- 功能强大稳定
- 设计统一
- 引入库体积
- 样式受限于库
- 学习成本
快速开发项目,或项目已集成某 UI 库。

最终建议:为了获得更好的用户体验和更统一的视觉设计,强烈推荐使用“自定义下拉列表”方案,如果你时间紧张,选择一个成熟的 UI 库是明智之举。