Vue.js 3中如何检测鼠标悬停元素时Shift键是否按下?
Vue3 多选组件批量选择实现方案
核心思路
放弃依赖@mouseover.shift这类修饰符,转而全局跟踪Shift键的按下/释放状态,结合鼠标悬停的选项实时计算高亮区间,再配合Shift+Click完成批量勾选。这种方式能覆盖「鼠标已在选项内时按下Shift」的场景,逻辑更完整。
具体实现步骤
1. 全局跟踪Shift键状态
在组件内定义响应式变量记录Shift键状态,通过监听全局键盘事件更新:
<script setup> import { ref, onMounted, onUnmounted } from 'vue' const isShiftPressed = ref(false) const lastSelectedIndex = ref(-1) // 记录最后一次选中的选项索引 const selectedItems = ref([]) // 已选中项ID集合 const options = ref([/* 你的选项数据,比如 {id: 1, label: '选项1'} */]) const handleKeyDown = (e) => { if (e.key === 'Shift') { isShiftPressed.value = true } } const handleKeyUp = (e) => { if (e.key === 'Shift') { isShiftPressed.value = false } } onMounted(() => { window.addEventListener('keydown', handleKeyDown) window.addEventListener('keyup', handleKeyUp) }) onUnmounted(() => { window.removeEventListener('keydown', handleKeyDown) window.removeEventListener('keyup', handleKeyUp) }) </script>
2. 鼠标悬停时高亮区间选项
给每个选项绑定鼠标悬停事件,结合Shift状态和最后选中索引,计算并标记需要高亮的项:
<template> <div v-for="(item, index) in options" :key="item.id" class="option-item" :class="{ 'highlight': shouldHighlight(index) }" @mouseover="hoveredIndex = index" @click="handleClick(index)" > <input type="checkbox" :checked="selectedItems.includes(item.id)"> <span>{{ item.label }}</span> </div> </template> <script setup> // ... 保留之前的代码 const hoveredIndex = ref(-1) const shouldHighlight = (index) => { if (!isShiftPressed.value || lastSelectedIndex.value === -1) return false const start = Math.min(lastSelectedIndex.value, hoveredIndex.value) const end = Math.max(lastSelectedIndex.value, hoveredIndex.value) return index >= start && index <= end } </script> <style scoped> .option-item { padding: 8px 12px; cursor: pointer; transition: background-color 0.2s; } .highlight { background-color: #e6f7ff; /* 自定义高亮背景色 */ } </style>
3. Shift+Click 批量勾选
在点击事件中区分普通点击和Shift+Click,实现单个切换或批量选中逻辑:
<script setup> // ... 保留之前的代码 const handleClick = (index) => { const itemId = options.value[index].id if (isShiftPressed.value && lastSelectedIndex.value !== -1) { // 批量选中区间内的所有项 const start = Math.min(lastSelectedIndex.value, index) const end = Math.max(lastSelectedIndex.value, index) const newSelected = new Set(selectedItems.value) for (let i = start; i <= end; i++) { newSelected.add(options.value[i].id) } selectedItems.value = Array.from(newSelected) } else { // 普通点击:切换单个项的选中状态 const idx = selectedItems.value.indexOf(itemId) if (idx > -1) { selectedItems.value.splice(idx, 1) } else { selectedItems.value.push(itemId) } // 更新最后选中的索引 lastSelectedIndex.value = index } } </script>
可选优化
- 支持反选:可以在Shift+Click时判断区间内的项是否已全部选中,若已全选则批量移除。
- 边界处理:当没有任何选中项时,Shift+Click直接选中当前项并更新最后选中索引。
内容的提问来源于stack exchange,提问作者jschpmr
相关产品推荐
相关产品推荐

