Vue.js Events: How to detect if key is pressed while the mouse is over a certain element?

924 Views Asked by At

I am currently working on a multi item selection component in Vue.js 3.

Each item is represented by a <div> element containing a checkbox.

For a feature I want to implement I need to detect when the Shift key is pressed while the mouse is over a <div>. (My goal is to have all the items between the item checked last and the item currently under the mouse highlighted while the Shift key is pressed, and then have them all checked with Shift + Click - but that is beside the point).

I tried to achieve this using @mouseover.shift like so:

    <div v-for="item in items"
         class="item"
         @mouseover.shift="onMouseOverShift(item)"
    >

Unfortunately, while @mouseover.shift works fine to detect whether Shift is pressed when the mouse enters the <div>, but does not trigger when the key is pressed while the mouse is already inside of the <div>.

Do you know what would be the best way to achieve what i am looking for?

2

There are 2 best solutions below

0
Alexandre Heinen On

I think you can use like this:

<div v-for="item in items"
    class="item"
    @mouseenter="isMouseOver = true"
    @mouseleave="isMouseOver = false"
    @keypress.shift="onMouseOverShift(item)"
/>

Then on the method onMouseOverShift you check if isMouseOver

0
Moritz Ringler On

I don't think you can detect a keypress on a div, or at least not reliably. You best bet is probably to combine the mouse events with a global key watcher.

In Vue 3 composition API, this would look something like this:

In your component setup:

const hoveredItem = ref(null)
const shiftPressed = useShiftKeyWatcher()
const selectedItem = computed(() => shiftPressed.value ? hoveredItem.value : null)

In the template:

    <div v-for="item in items"
         class="item"
         @mouseenter="selectedItem = item"
         @mouseleave="selectedItem = null"
    >...</div>

Then the composable for useShiftKeyWatcher would look like:

import { ref, onMounted, onUnmounted } from "vue";

export function useShiftKeyWatcher() {
  const shiftPressed = ref(false);

  function checkKeyDown(event) {
    shiftPressed.value = event.key === "Shift";
  }

  function checkKeyUp(event) {
    shiftPressed.value &&= event.key !== "Shift";
  }

  onMounted(() => window.addEventListener("keydown", checkKeyDown));
  onMounted(() => window.addEventListener("keyup", checkKeyUp));
  onUnmounted(() => window.removeEventListener("keydown", checkKeyDown));
  onUnmounted(() => window.removeEventListener("keyup", checkKeyUp));

  return shiftPressed;
}

Here is a sandbox (you need to click into the preview area once to have key events go to the inner document)