177

I would like to show a div when hovering over an element in vue.js. But I can't seem to get it working.

It looks like there is no event for hover or mouseover in vue.js. Is this really true?

Would it be possible to combine jquery hover and vue methods?

Penny Liu
  • 15,447
  • 5
  • 79
  • 98
Anders Andersen
  • 2,377
  • 2
  • 17
  • 25
  • 2
    v-on directive works for "hover" event also. If you add to your question the code you've written we can probably help you get it working. And yes, Vue is simple and small and intended to be integrated with other packages like jQuery. – David K. Hess Jun 20 '15 at 14:51

15 Answers15

255

i feel above logics for hover is incorrect. it just inverse when mouse hovers. i have used below code. it seems to work perfectly alright.

<div @mouseover="upHere = true" @mouseleave="upHere = false" >
  <h2> Something Something </h2>
  <some-component v-show="upHere"></some-component>
</div>

on vue instance

data: {
  upHere: false
}
starball
  • 20,030
  • 7
  • 43
  • 238
shakee93
  • 4,976
  • 2
  • 28
  • 32
  • 20
    This should be the accepted answer! The accepted and the most upvoted answer do result in a flickering component. Every movement of the cursor on the @mouseover-div inverts the current state... – Stefan Medack Sep 12 '17 at 14:09
  • If you're showing a hidden div like a speech bubble, you'll get flickering on mouseover. Just add the same awesome mouseover/mouseleave code to the hidden div as well. – mcmacerson Dec 02 '17 at 23:40
  • Works for me, with webpack just have to change your data as : `data: () => ({ upHere: false })` – Emile Cantero May 03 '18 at 12:42
  • 1
    For anyone struggling from flickering when child is hovered use mouseenter event instead of mouseover – Sovetnikov Mar 04 '21 at 09:00
131

Here is a working example of what I think you are asking for.

http://jsfiddle.net/1cekfnqw/3017/

 <div id="demo">
        <div v-show="active">Show</div>
        <div @mouseover="mouseOver">Hover over me!</div>
    </div>

var demo = new Vue({
    el: '#demo',
    data: {
        active: false
    },
    methods: {
        mouseOver: function(){
            this.active = !this.active;   
        }
    }
});
Duka
  • 513
  • 1
  • 6
  • 19
Jarrod
  • 1,526
  • 1
  • 10
  • 6
  • 28
    does not work with newest vue version. @CYB tried to edit your answer to `v-on:mouseover="mouseOver"` but did not mention in which version of vue the syntax changed – Aprillion Aug 07 '16 at 12:47
  • 3
    @NICO has a better solution than mine and works with the current version (1.0.26 at the time of this post). Please reference his answer. Thank you. – Jarrod Aug 08 '16 at 15:58
  • 1
    I would like to delete this since I just stated, the post below by @NICO is better than mine, and more up to date. Please Give NICO that correct answer, and I will delete mine. Thank you! – Jarrod Aug 08 '16 at 16:01
  • 2
    the example is broken? – user3743266 Jul 28 '17 at 14:11
  • 7
    i think better use short hand ```@mouseover:mouseover``` – Davod Aslani Fakor Aug 22 '17 at 10:22
86

There's no need for a method here.

HTML

<div v-if="active">
    <h2>Hello World!</h2>
 </div>

 <div v-on:mouseover="active = !active">
    <h1>Hover me!</h1>
 </div>

JS

new Vue({
  el: 'body',
  data: {
    active: false
  }
})
NICO
  • 1,695
  • 1
  • 12
  • 11
  • 11
    You can use either `v-on:mouseover` or the shortcut `@mouseover` per the docs http://vuejs.org/guide/syntax.html#v-on-Shorthand – nu everest Aug 29 '16 at 01:42
  • 1
    You can replace `on` with `v-on:` or `@` for any of the html event attributes. http://www.w3schools.com/tags/ref_eventattributes.asp – nu everest Aug 29 '16 at 01:50
  • What's the problem? This one is working and should be marked as the correct answer. – NICO Aug 29 '16 at 02:07
  • Vue 2.2.0 don't like this - spit a warning "[Vue warn]: Do not mount Vue to or - mount to normal elements instead. " – Dima Fomin Mar 27 '17 at 21:37
  • For the sake of simplicity i made a vue instance. Of course you shouldnt do that in your real world application. – NICO Mar 28 '17 at 09:48
26

To show child or sibling elements it's possible with CSS only. If you use :hover before combinators (+, ~, >, space). Then the style applies not to hovered element.

HTML

<body>
  <div class="trigger">
    Hover here.
  </div>
  <div class="hidden">
    This message shows up.
  </div>
</body>

CSS

.hidden { display: none; }
.trigger:hover + .hidden { display: inline; }
qsc vgy
  • 261
  • 3
  • 2
  • 2
    The questioner is specifically asking about vue.js. Since it allows javascript to be easily bound to the mouseover event. – nu everest Aug 28 '16 at 20:32
  • 7
    I'm using Vue and this is the best solution for me. I have a nested list with buttons that should only appear on hover, and using extra variables to track the hover state is overkill. CSS is much more elegant. Thanks qsc! – david_nash Mar 23 '17 at 04:16
19

With mouseover and mouseleave events you can define a toggle function that implements this logic and react on the value in the rendering.

Check this example:

var vm = new Vue({
 el: '#app',
 data: {btn: 'primary'}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">


<div id='app'>
    <button
        @mouseover="btn='warning'"
        @mouseleave="btn='primary'"
        :class='"btn btn-block btn-"+btn'>
        {{ btn }}
    </button>
</div>
user2976753
  • 935
  • 11
  • 24
fitorec
  • 4,257
  • 2
  • 24
  • 18
  • css postprocessors e.g. purgecss will not be able to pick up your classes if you dynamically build them like this. better: `@mouseover="btn-color='btn-warning' @mouseleave="btn-color='btn-primary' :class="btn btn-block { btn-color}"` – Erich Jan 11 '20 at 15:05
13

I think what you want to achieve is with the combination of

@mouseover, @mouseout, @mouseenter and @mouseleave

So the two best combinations are

"@mouseover and @mouseout"

or

"@mouseenter and @mouseleave"

And I think, It's better to use the 2nd pair so that you can achieve the hover effect and call functionalities on that.

    <div @mouseenter="activeHover = true" @mouseleave="activeHover = false" >
        <p v-if="activeHover"> This will be showed on hover </p>
        <p v-if ="!activeHover"> This will be showed in simple cases </p>
    </div>    

on vue instance

    data : {
        activeHover : false
    }

Note: 1st pair will effect/travel on the child elements as well but 2nd pair will only effect where you want to use it not the child elements. Else you will experience some glitch/fluctuation by using 1st pair. So, better to use 2nd pair to avoid any fluctuations.

I hope, it will help others as well :)

Penny Liu
  • 15,447
  • 5
  • 79
  • 98
Shamsail
  • 612
  • 6
  • 17
10

Though I would give an update using the new composition api.

Component

<template>
  <div @mouseenter="hovering = true" @mouseleave="hovering = false">
    {{ hovering }}
  </div>
</template>

<script>
  import { ref } from '@vue/composition-api'

  export default {
    setup() {
      const hovering = ref(false)
      return { hovering }
    }
  })
</script>

Reusable Composition Function

Creating a useHover function will allow you to reuse in any components.

export function useHover(target: Ref<HTMLElement | null>) {
  const hovering = ref(false)

  const enterHandler = () => (hovering.value = true)
  const leaveHandler = () => (hovering.value = false)

  onMounted(() => {
    if (!target.value) return
    target.value.addEventListener('mouseenter', enterHandler)
    target.value.addEventListener('mouseleave', leaveHandler)
  })

  onUnmounted(() => {
    if (!target.value) return
    target.value.removeEventListener('mouseenter', enterHandler)
    target.value.removeEventListener('mouseleave', leaveHandler)
  })

  return hovering
}

Here's a quick example calling the function inside a Vue component.

<template>
  <div ref="hoverRef">
    {{ hovering }}
  </div>
</template>

<script lang="ts">
  import { ref } from '@vue/composition-api'
  import { useHover } from './useHover'

  export default {
    setup() {
      const hoverRef = ref(null)
      const hovering = useHover(hoverRef)
      return { hovering, hoverRef }
    }
  })
</script>

You can also use a library such as @vuehooks/core which comes with many useful functions including useHover.

Reference: Vuejs composition API

Community
  • 1
  • 1
jsbroks
  • 530
  • 6
  • 15
8

It's possible to toggle a class on hover strictly within a component's template, however, it's not a practical solution for obvious reasons. For prototyping on the other hand, I find it useful to not have to define data properties or event handlers within the script.

Here's an example of how you can experiment with icon colors using Vuetify.

new Vue({
  el: '#app'
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.js"></script>

<div id="app">
  <v-app>
    <v-toolbar color="black" dark>
      <v-toolbar-items>
        <v-btn icon>
          <v-icon @mouseenter="e => e.target.classList.toggle('pink--text')" @mouseleave="e => e.target.classList.toggle('pink--text')">delete</v-icon>
        </v-btn>
        <v-btn icon>
          <v-icon @mouseenter="e => e.target.classList.toggle('blue--text')" @mouseleave="e => e.target.classList.toggle('blue--text')">launch</v-icon>
        </v-btn>
        <v-btn icon>
          <v-icon @mouseenter="e => e.target.classList.toggle('green--text')" @mouseleave="e => e.target.classList.toggle('green--text')">check</v-icon>
        </v-btn>
      </v-toolbar-items>
    </v-toolbar>
  </v-app>
</div>
Brian Lee
  • 17,904
  • 3
  • 41
  • 52
7

With mouseover only the element stays visible when mouse leaves the hovered element, so I added this:

@mouseover="active = !active" @mouseout="active = !active"

<script>
export default {
  data(){
   return {
     active: false
   }
 }
</script>
besthost
  • 759
  • 9
  • 14
2

I came up with the same problem, and I work it out !

        <img :src='book.images.small' v-on:mouseenter="hoverImg">
2

There is a correct working JSFiddle: http://jsfiddle.net/1cekfnqw/176/

<p v-on:mouseover="mouseOver" v-bind:class="{on: active, 'off': !active}">Hover over me!</p>
funkvn
  • 21
  • 2
2

Please take a look at the vue-mouseover package if you are not satisfied with how does this code look:

<div
    @mouseover="isMouseover = true"
    @mouseleave="isMouseover = false"
/>

vue-mouseover provides a v-mouseover directive that automaticaly updates the specified data context property when the cursor enters or leaves an HTML element the directive is attached to.

By default in the next example isMouseover property will be true when the cursor is over an HTML element and false otherwise:

<div v-mouseover="isMouseover" />

Also by default isMouseover will be initially assigned when v-mouseover is attached to the div element, so it will not remain unassigned before the first mouseenter/mouseleave event.

You can specify custom values via v-mouseover-value directive:

<div
    v-mouseover="isMouseover"
    v-mouseover-value="customMouseenterValue"/>

or

<div
    v-mouseover="isMouseover"
    v-mouseover-value="{
        mouseenter: customMouseenterValue,
        mouseleave: customMouseleaveValue
    }"
/>

Custom default values can be passed to the package via options object during setup.

N. Kudryavtsev
  • 3,556
  • 1
  • 26
  • 30
0

Here is a very simple example for MouseOver and MouseOut:

<div id="app">
   <div :style = "styleobj" @mouseover = "changebgcolor" @mouseout = "originalcolor"> 
   </div>
</div>

new Vue({
  el:"#app",
  data:{
     styleobj : {
       width:"100px",
       height:"100px",
       backgroundColor:"red"
     }
  },
  methods:{
    changebgcolor : function() {
      this.styleobj.backgroundColor = "green";
    },
    originalcolor : function() {
      this.styleobj.backgroundColor = "red";
    }
  }
});
Hardik Raval
  • 3,406
  • 1
  • 26
  • 28
0

This worked for me for nuxt

<template>
  <span
    v-if="item"
    class="primary-navigation-list-dropdown"
    @mouseover="isTouchscreenDevice ? null : openDropdownMenu()"
    @mouseleave="isTouchscreenDevice ? null : closeDropdownMenu()"
  >
    <nuxt-link
      to="#"
      @click.prevent.native="openDropdownMenu"
      v-click-outside="closeDropdownMenu"
      :title="item.title"
      :class="[
        item.cssClasses,
        { show: isDropdownMenuVisible }
      ]"
      :id="`navbarDropdownMenuLink-${item.id}`"
      :aria-expanded="[isDropdownMenuVisible ? true : false]"
      class="
        primary-navigation-list-dropdown__toggle
        nav-link
        dropdown-toggle"
      aria-current="page"
      role="button"
      data-toggle="dropdown"
    >
      {{ item.label }}
    </nuxt-link>
    <ul
      :class="{ show: isDropdownMenuVisible }"
      :aria-labelledby="`navbarDropdownMenuLink-${item.id}`"
      class="
        primary-navigation-list-dropdown__menu
        dropdown-menu-list
        dropdown-menu"
    >
      <li
        v-for="item in item.children" :key="item.id"
        class="dropdown-menu-list__item"
      >
        <NavLink
          :attributes="item"
          class="dropdown-menu-list__link dropdown-item"
        />
      </li>
    </ul>
  </span>
</template>

<script>
import NavLink from '@/components/Navigation/NavLink';

export default {
  name: "DropdownMenu",
  props: {
    item: {
      type: Object,
      required: true,
    },
  },
  data() {
    return {
      isDropdownMenuVisible: false,
      isTouchscreenDevice: false
    };
  },
  mounted() {
    this.detectTouchscreenDevice();
  },
  methods: {
    openDropdownMenu() {
      if (this.isTouchscreenDevice) {
        this.isDropdownMenuVisible = !this.isDropdownMenuVisible;
      } else {
        this.isDropdownMenuVisible = true;
      }
    },

    closeDropdownMenu() {
      if (this.isTouchscreenDevice) {
        this.isDropdownMenuVisible = false;
      } else {
        this.isDropdownMenuVisible = false;
      }
    },

    detectTouchscreenDevice() {
      if (window.PointerEvent && ('maxTouchPoints' in navigator)) {
        if (navigator.maxTouchPoints > 0) {
          this.isTouchscreenDevice = true;
        }
      } else {
        if (window.matchMedia && window.matchMedia("(any-pointer:coarse)").matches) {
          this.isTouchscreenDevice = true;
        } else if (window.TouchEvent || ('ontouchstart' in window)) {
          this.isTouchscreenDevice = true;
        }
      }
      return this.isTouchscreenDevice;
    }
  },
  components: {
    NavLink
  }
};
</script>

<style scoped lang="scss">
.primary-navigation-list-dropdown {
  &__toggle {
    color: $white;

    &:hover {
      color: $blue;
    }
  }

  &__menu {
    margin-top: 0;
  }

  &__dropdown {

  }
}

.dropdown-menu-list {
  &__item {

  }

  &__link {
    &.active,
    &.nuxt-link-exact-active {
      border-bottom: 1px solid $blue;
    }
  }
}
</style>
atazmin
  • 4,757
  • 1
  • 32
  • 23
0

You can also use VueUse composables.

This is for mouse hover

<script setup>
import { useElementHover } from '@vueuse/core'

const myHoverableElement = ref()
const isHovered = useElementHover(myHoverableElement)
</script>

<template>
  <button ref="myHoverableElement">
    {{ isHovered }}
  </button>
</template>

This one for mouse hover

import { useMouse } from '@vueuse/core'

const { x, y, sourceType } = useMouse()

or even in a specific element.

TLDR: quite a few handy composables for your Vue2/Vue3 apps!

kissu
  • 40,416
  • 14
  • 65
  • 133