Mouseover or hover vue.js

412.9k views Asked by At

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?

15

There are 15 answers

5
NICO On BEST ANSWER

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
  }
})
0
atazmin On

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>
0
Shamsail On

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 :)

0
besthost On

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>
1
fitorec On

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>

0
Hardik Raval On

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";
    }
  }
});
0
funkvn On

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>
0
Brian Lee On

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>

0
N. Kudryavtsev On

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.

4
shakee93 On

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
}
0
LittleStupid On

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

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

5
Jarrod On

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

https://jsfiddle.net/m2w6egn9/

 <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;   
        }
    }
});
0
kissu On

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!

0
jsbroks On

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

2
qsc vgy On

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; }