Skip to content

Select 选择器

响应式选择器组件。

行内 Tailwind/UnoCSS 用法

使用行内的tailwind/unocss样式构建,复制组件的代码即可使用

查看代码
html
<!-- 基础选择器 -->
<select class="w-full rounded-md border border-solid border-gray-300 bg-white px-4 py-2 text-gray-700 shadow-sm focus:outline-none focus:ring-1 focus:ring-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300">
  <option selected>
    请选择
  </option>
  <option>选项 1</option>
  <option>选项 2</option>
  <option>选项 3</option>
</select>

自定义下拉菜单

需要javascript 控制下拉菜单显示和隐藏

查看代码
html
<div class="tailuno-select group relative w-[200px]" data-open="false">
  <button class="select-button flex w-full items-center justify-between rounded-md border border-solid border-gray-300 bg-white px-4 py-2 text-left text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-1 focus:ring-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700">
    <span>请选择</span>
    <svg class="size-5 text-gray-400 transition-transform duration-200 group-data-[open=true]:rotate-180"
      fill="none"
      stroke="currentColor"
      viewBox="0 0 24 24">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
    </svg>
  </button>
  <div class="select-dropdown absolute z-10 mt-1 hidden max-h-60 w-full overflow-auto rounded-md bg-white shadow-lg group-data-[open=true]:block dark:border-gray-600 dark:bg-gray-800">
    <div class="select-option option-item cursor-pointer px-4 py-2 text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700 [&[data-selected='true']]:bg-gray-100 [&[data-selected='true']]:dark:bg-blue-900/50 [&[data-selected='true']]:dark:text-blue-400">
      选项 1
    </div>
    <div class="select-option option-item cursor-pointer px-4 py-2 text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700 [&[data-selected='true']]:bg-gray-100 [&[data-selected='true']]:dark:bg-blue-900/50 [&[data-selected='true']]:dark:text-blue-400">
      选项 2
    </div>
    <div class="select-option option-item cursor-pointer px-4 py-2 text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700 [&[data-selected='true']]:bg-gray-100 [&[data-selected='true']]:dark:bg-blue-900/50 [&[data-selected='true']]:dark:text-blue-400">
      选项 3
    </div>
    <div class="select-option option-item cursor-pointer px-4 py-2 text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700 [&[data-selected='true']]:bg-gray-100 [&[data-selected='true']]:dark:bg-blue-900/50 [&[data-selected='true']]:dark:text-blue-400">
      选项 4
    </div>
  </div>
</div>
js
// 全局事件处理器,引入此文件自动注册,只注册一次
// 全局入口或组件中引入此文件皆可,不限html或vue或react
function initGlobalSelectHandler() {
  if (window._selectHandlerInitialized)
    return

  document.addEventListener('click', (e) => {
    const selectContainer = e.target.closest('.tailuno-select')
    if (!selectContainer) {
      // 点击外部,关闭所有下拉框
      document.querySelectorAll('.tailuno-select[data-open="true"]')
        .forEach(container => container.dataset.open = 'false')
      return
    }

    const button = e.target.closest('button')
    const option = e.target.closest('.option-item')

    if (button) {
      // 切换下拉框状态
      const isOpen = selectContainer.dataset.open === 'true'
      selectContainer.dataset.open = !isOpen
    }
    else if (option) {
      // 更新选中状态
      selectContainer.querySelectorAll('.option-item').forEach((item) => {
        item.removeAttribute('data-selected')
      })
      option.setAttribute('data-selected', 'true')

      // 更新显示文本
      selectContainer.querySelector('button span').textContent = option.textContent

      // 关闭下拉框
      selectContainer.dataset.open = 'false'
    }
  })

  window._selectHandlerInitialized = true
}

initGlobalSelectHandler()
vue
<!-- Vue版本,直接复制此文件使用即可,不使用点击外侧自动收起的话,无需再引入其他js -->
<script setup>
import { onMounted, onUnmounted, ref } from 'vue'

const options = [
  { label: '选项 1', value: '1' },
  { label: '选项 2', value: '2' },
  { label: '选项 3', value: '3' },
  { label: '选项 4', value: '4' },
]

const isOpen = ref(false)
const selected = ref('')

function toggleOpen() {
  isOpen.value = !isOpen.value
}

function selectOption(option) {
  selected.value = option
  isOpen.value = false
}

// 可选,点击外部时关闭下拉菜单
const selectRef = ref(null)
const listener = event => (!selectRef.value?.contains(event.target) && (isOpen.value = false))
onMounted(() => document.addEventListener('click', listener))
onUnmounted(() => document.removeEventListener('click', listener))
</script>

<template>
  <div ref="selectRef" class="relative w-[200px]">
    <button class="flex w-full items-center justify-between rounded-md border border-solid border-gray-300 bg-white px-4 py-2 text-left text-gray-700 shadow-sm hover:bg-gray-50 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-primary dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700"
      @click="toggleOpen">
      <span>{{ selected?.label || '请选择' }}</span>
      <svg class="size-5 text-gray-400 transition-transform duration-200"
        :class="{ 'rotate-180': isOpen }"
        fill="none"
        stroke="currentColor"
        viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
      </svg>
    </button>
    <div v-show="isOpen"
      class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white shadow-lg dark:border-gray-600 dark:bg-gray-800">
      <div v-for="option in options"
        :key="option"
        class="cursor-pointer px-4 py-2 text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700"
        :class="{ 'bg-blue-50 text-blue-600 dark:bg-blue-900/50 dark:text-blue-400': selected?.value === option.value }"
        @click="selectOption(option)">
        {{ option.label }}
      </div>
    </div>
  </div>
</template>

CSS 类用法

使用预定义的CSS类。复制index.css的样式即可使用

查看代码
html
<!-- 基础选择器 -->
<select class="select">
  <option selected>
    请选择
  </option>
  <option>选项 1</option>
  <option>选项 2</option>
  <option>选项 3</option>
</select>

<!-- 带图标的选择器 -->
<div class="select-wrapper">
  <select class="select">
    <option selected>
      请选择
    </option>
    <option>选项 1</option>
    <option>选项 2</option>
    <option>选项 3</option>
  </select>
  <svg class="select-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l4-4 4 4m0 6l-4 4-4-4" />
  </svg>
</div>

<!-- 自定义下拉菜单 -->
<div class="select-custom-wrapper">
  <button class="select-custom-trigger" @click="toggleOpen">
    <span>{{ selected || '请选择' }}</span>
    <svg class="select-custom-arrow" :class="{ 'rotate-180': isOpen }" fill="none" stroke="currentColor" viewBox="0 0 24 24">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
    </svg>
  </button>
  <div v-show="isOpen" class="select-custom-dropdown">
    <div
      v-for="option in options"
      :key="option"
      class="select-custom-option"
      :class="{ 'select-custom-selected': selected === option }"
      @click="selectOption(option)">
      {{ option }}
    </div>
  </div>
</div>
css
.select {
  @apply block w-full px-4 py-2 text-gray-700 bg-white border border-solid border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-gray-300 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300;

  &:disabled {
    @apply opacity-50 cursor-not-allowed;
  }
}

.select-wrapper {
  @apply relative;
}

.select-icon {
  @apply absolute right-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 pointer-events-none;
}

.select-custom-wrapper {
  @apply relative w-full;
}

.select-custom-trigger {
  @apply w-full px-4 py-2 text-left text-gray-700 bg-white border border-solid border-gray-300 rounded-md shadow-sm flex items-center justify-between hover:bg-gray-50 focus:outline-none focus:ring-1 focus:ring-gray-300 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-700;
}

.select-custom-arrow {
  @apply w-5 h-5 text-gray-400 transition-transform duration-200;
}

.select-custom-dropdown {
  @apply absolute z-[10] w-full mt-1 bg-white rounded-md shadow-lg max-h-60 overflow-auto dark:bg-gray-800 dark:border-gray-600;
}

.select-custom-option {
  @apply px-4 py-2 text-gray-700 hover:bg-gray-100 cursor-pointer dark:text-gray-300 dark:hover:bg-gray-700;

  &.select-custom-selected {
    @apply bg-gray-100 dark:bg-blue-900/50 dark:text-blue-400;
  }
}