Vue.js中如何为select的option标签添加CSS实现自定义下拉框
Vue.js 实现可完全自定义样式的select下拉选择框
原生<select>组件的下拉弹出层、选项样式由浏览器原生控件渲染,CSS无法深度修改,这是你无法给元素加自定义样式的根本原因。要实现和参考图一致的自定义样式,需要放弃原生select标签,用普通DOM元素模拟下拉交互逻辑,所有元素都支持任意CSS修改。

核心实现思路
你只需要实现几个基础交互逻辑,就能得到一个完全可控的自定义下拉框:
- 点击触发区域切换下拉选项的显示/隐藏
- 点击选项时更新选中值、同步显示文本、自动收起下拉
- 点击下拉组件外部区域时自动收起下拉
- 所有结构用div、ul、li这类普通标签实现,样式完全自定义
可直接复用的单文件组件代码
<template> <div class="custom-select" ref="selectRef"> <!-- 选中值展示触发区 --> <div class="select-trigger" @click="toggleDropdown"> <span class="selected-text">{{ selectedLabel || placeholder }}</span> <span class="arrow" :class="{ 'arrow-up': isOpen }">▼</span> </div> <!-- 下拉选项列表 --> <ul class="options-list" v-show="isOpen"> <li v-for="option in options" :key="option.value" class="option-item" :class="{ 'is-selected': option.value === modelValue }" @click="handleSelect(option)" > {{ option.label }} </li> </ul> </div> </template> <script setup> import { ref, onMounted, onBeforeUnmount } from 'vue' const props = defineProps({ modelValue: [String, Number], options: { type: Array, required: true }, placeholder: { type: String, default: '请选择' } }) const emit = defineEmits(['update:modelValue']) const isOpen = ref(false) const selectRef = ref(null) const selectedLabel = ref('') const toggleDropdown = () => isOpen.value = !isOpen.value const handleSelect = (option) => { emit('update:modelValue', option.value) selectedLabel.value = option.label isOpen.value = false } // 点击组件外部关闭下拉 const handleClickOutside = (e) => { if (selectRef.value && !selectRef.value.contains(e.target)) isOpen.value = false } onMounted(() => { // 初始化回显已选值 const matched = props.options.find(item => item.value === props.modelValue) if (matched) selectedLabel.value = matched.label document.addEventListener('click', handleClickOutside) }) onBeforeUnmount(() => { document.removeEventListener('click', handleClickOutside) }) </script> <style scoped> .custom-select { position: relative; width: 240px; font-size: 14px; } /* 触发框样式,可任意修改匹配设计稿 */ .select-trigger { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; border: 1px solid #dcdfe6; border-radius: 6px; cursor: pointer; background: #fff; } .select-trigger:hover { border-color: #409eff; } .selected-text { color: #303133; } .selected-text:empty::after { content: attr(placeholder); color: #c0c4cc; } .arrow { font-size: 12px; color: #909399; transition: transform 0.2s; } .arrow-up { transform: rotate(180deg); } /* 下拉列表容器样式 */ .options-list { position: absolute; top: calc(100% + 6px); left: 0; width: 100%; margin: 0; padding: 6px 0; list-style: none; background: #fff; border: 1px solid #e4e7ed; border-radius: 6px; box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1); z-index: 999; } /* 单个选项样式 */ .option-item { padding: 8px 14px; cursor: pointer; color: #606266; } .option-item:hover { background-color: #f5f7fa; } /* 选中项高亮样式 */ .option-item.is-selected { color: #409eff; font-weight: 500; background-color: #ecf5ff; } </style>
组件使用方式
在需要用到下拉框的页面引入组件即可,支持v-model双向绑定:
<template> <CustomSelect v-model="selectedCity" :options="cityOptions" placeholder="请选择城市" /> </template> <script setup> import { ref } from 'vue' import CustomSelect from './CustomSelect.vue' const selectedCity = ref('') const cityOptions = ref([ { label: '北京', value: 'beijing' }, { label: '上海', value: 'shanghai' }, { label: '广州', value: 'guangzhou' }, { label: '深圳', value: 'shenzhen' } ]) </script>
补充说明
- 上述组件的所有样式都可以自由修改,你可以调整圆角、边框、颜色、阴影、间距等所有视觉属性,完全匹配你提供的参考设计图,没有原生select的样式限制
- 如果需要搜索过滤、多选、选项分组、虚拟滚动等复杂功能,可以直接在这个基础逻辑上扩展
- 如果不想从零手写,也可以直接使用Element Plus、Naive UI等成熟Vue组件库的Select组件,这类组件同样是用DOM模拟实现,支持任意样式自定义。
内容的提问来源于stack exchange,提问作者Deepshikha Dudi
相关产品推荐
相关产品推荐

