Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue: How can you tell whether the control key is pressed or not in a performant manner during mouseover?

Here is my first attempt at keeping track of whether the control key (ctrl) is being pressed while the mouse is over a <section>:

<template>
  <section v-on:mouseover="controlKeyPressed = $event.ctrl">...</section>
</template>

That seems like it'd be extremely bad for performance, because every time the user moves the mouse, this code needs to fire. Ideally, there would be code that runs only when the ctrl key is pressed/released.

like image 588
Adam Zerner Avatar asked Jan 02 '23 17:01

Adam Zerner


2 Answers

You can listen for keyboard events specifically. From the example code in the vue.js documentation:

<!-- only call `vm.submit()` when the `keyCode` is 13 -->
<input v-on:keyup.13="submit">

So you would listen for both keyup and keydown events for the ctrl key, and store the current state in your handler function.

like image 189
fennel Avatar answered Jan 05 '23 16:01

fennel


That seems like it'd be extremely bad for performance, because every time the user moves the mouse, this code needs to fire.

Before entering the mouseover details, even if that event runs often, you shouldn't have such a performance hit. Code like this runs every little second by the browser for a number of reasons (and libs/plugins/extensions/trackers). Unless you don't do any intense calculation on the handler, there should be no problem.

Ultimately, though, this is the kind of thing you shouldn't worry until you have numbers.

Ideally, there would be code that runs only when the ctrl key is pressed/released.

There are the mouseenter and mouseleave events. But it doesn't seem to be what you are looking for. See how they behave:

new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue.js!',
  },
  methods: {
    imIn() {
      console.log('Mouse enters with CTRL')
    },
    imOut() {
      console.log('Mouse leaves with CTRL')
    }
  }
})
section { background: blue; height: 30px; width: 100px; }
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>

<div id="app">
  <section @mouseenter.ctrl="imIn" @mouseleave.ctrl="imOut"></section>
</div>

Actually, mouseover doesn't run every time

MDN says:

The mouseover event is fired when a pointing device is moved onto the element that has the listener attached or onto one of its children.

So it is not ran every time. Only when the mouse:

  • moves onto the element; or
  • moves onto a child of the element

Have a look at the boxes below.

new Vue({
  el: '#app',
  methods: {
    mouseOverElementWithChildren() {
      console.log('Mouse Over Element with Children', new Date())
    },
    mouseOverElementWithoutChildren() {
      console.log('Mouse Over Element WITHOUT Children', new Date())
    }
  }
})
.nonempty { border: 1px solid red; }
.nonempty * { border: 1px solid blue; width: 200px; }

.empty { border: 1px solid green; height: 40px; }
* { font-size: small; font-family: verdana }
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>

<div id="app">
  Hold CTRL and move the mouse over the elements below.<br><br>
  
  This one has many children. Notice the MOUSEOVER only triggers when you move over a line (that is, enters/leaves the element or one of its children).
  <section @mouseover.ctrl="mouseOverElementWithChildren" class="nonempty">
    <ul>
      <li>a a a</li>
      <li>b b b</li>
    </ul>
  </section>

  <br> This one has no children. Notice the MOUSEOVER only triggers when you ENTER/LEAVE the element.
  <section @mouseover.ctrl="mouseOverElementWithoutChildren" class="empty"></section>
  <br><br><br><br><br><br><br>
</div>

A workaround for elements with many children

So, if you don't want your mouseover code to run every time, either create an empty element (which is probably not what you want), or create an overlay on top of that element and hook the mouseover event to that overlay.

That would require some CSS work, but is doable:

new Vue({
  el: '#app',
  methods: {
    mouseOverElementWithChildren() {
      console.log('Mouse Over BLUE', new Date())
    },
    mouseOverElementWithoutChildren() {
      console.log('Mouse Over GREEN', new Date())
    }
  }
})
.nonempty { border: 1px solid red; height: 70% }
.nonempty * { border: 1px solid blue; }
.empty { border: 1px solid green; width: 80%; height: 80%; }
/* below is from https://stackoverflow.com/a/2941203/1850609 */
#app { width: 100px; height: 100px; position: relative; border: 1px solid orange; }
.nonempty, .empty { position: absolute; top: 0; left: 0; }
.empty { z-index: 10; }
* { font-size: x-small; font-family: verdana }
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>

Hold CTRL and move the mouse over the elements below.<br>Whole #app is orange border. Green is on top of the blue. Notice the blue does not trigger mouseOver events, and green only triggers once.
<div id="app">
  
  
  <section @mouseover.ctrl="mouseOverElementWithChildren" class="nonempty">
    <ul>
      <li>a a a</li>
      <li>b b b</li>
    </ul>
  </section>

  <section @mouseover.ctrl="mouseOverElementWithoutChildren" class="empty"></section>
  <br><br><br><br><br><br><br>
</div>
like image 44
acdcjunior Avatar answered Jan 05 '23 15:01

acdcjunior