18

I can successfully add a class on mouseover with Vue. But I want to remove the class when the mouse leaves the element. What is the idiomatic way of handling this in Vue?

<template>
  <div id="navigation">
    <div class="nav-container">
      <nav>
        <router-link to="/" class="home">Ping Party</router-link>
        <div class="navigation-actions">
          <router-link to="/sign_in" v-if="!isLoggedIn" class="sign_in">Sign In</router-link>
          <router-link to="/sign_up" v-if="!isLoggedIn" @mouseover.native="mouseOver" class="sign_up" ref="sign_up">Sign Up</router-link>
          <router-link to="/users" v-if="isLoggedIn" class="users">Users</router-link>
          <v-btn :click.prevent="signOut()" v-if="isLoggedIn">Sign Out</v-btn>
        </div>
      </nav>
    </div>
  </div>
</template>

<script>
  import SignUp from "../forms/SignUp";
  import SignIn from "../forms/SignIn";

  export default {
    components: {
      SignUp,
      SignIn
    },
    computed: {
      isLoggedIn () {
        return this.$store.getters.isLoggedIn
      }
    },
    methods: {
      signOut: function() {
        this.$store.commit("LOGOUT")
        this.$store.commit("FLASH_MESSAGE", {
          message: "Signed Out Successfully",
          show: true,
          styleClass: "error",
          timeOut: 4000
        })
        this.$router.push('/')
      },
      mouseOver: function() {
        this.$refs.sign_up.$vnode.elm.classList.add("hovered")
      }
    }
  }
</script>

As you can see on mouseover I call the mouseOver function and this successfully adds the class to the element. But now when the users leaves the element the class remains. How can I have the class remove when the user leaves the element? Thanks for the help.

4 Answers 4

31

Listen for both mouseover and mouseout and set the class based on that.

console.clear()

new Vue({
  el: "#app",
  data:{
    isHovering: false
  }
})
.hovering{
  color: red
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
  <h1 @mouseover="isHovering = true" 
      @mouseout="isHovering = false" 
      :class="{hovering: isHovering}">
    {{ isHovering ? "Woot! Hovered" : "Hover over me" }}
  </h1>
</div>

Or just use CSS.

console.clear()

new Vue({
  el: "#app",
  data:{
    isHovering: false
  }
})
h1:hover{
  color: red
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
  <h1 @mouseover="isHovering = true" 
      @mouseout="isHovering = false" >
    {{ isHovering ? "Woot! Hovered" : "Hover over me" }}
  </h1>
</div>

Sign up to request clarification or add additional context in comments.

6 Comments

But please keep in mind there's a :hover in CSS OP. This is completely overkill. So .sign_up:hover { color: red; } will give you the same result much more cleanly.
@BillCriswell Yeah... That would make too much sense :)
Yea, That does make sense. I'm just trying to grok how to do things like this in Vue. But, yes this could be done a lot easier.
Another proof how powerful CSS is these days!
While @BillCriswell's comment is valid, there is an important use case for this manual approach: css animations. The spec didn't include a reset function, so there's no 'un-hover' event, and animations can't be restarted without applying and removing a class (at least, not with full control). This method above is a good way of doing it, when you need exit-animations.
|
15

A more scalable solution would be to use a directive:

// Directives
Vue.directive('add-class-hover', {
  bind(el, binding, vnode) {    
    const { value="" } = binding;
    el.addEventListener('mouseenter',()=> {
        el.classList.add(value)
    });
    el.addEventListener('mouseleave',()=> {
        el.classList.remove(value)
    });
  },
  unbind(el, binding, vnode) {
    el.removeEventListener('mouseenter');
    el.removeEventListener('mouseleave')
  }
})

new Vue({
  el: "#app"
})
.hoverClass {
color: red;
font-weight: 700;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <h1 v-add-class-hover="'hoverClass'">
    Text
  </h1>
</div>

3 Comments

Thanks for this nice solution. For unbind, remove event listener require a listener as second parameter: developer.mozilla.org/en-US/docs/Web/API/EventTarget/…
In the end my solution was to call the method again at unbind: ``` unbind(el, binding, vnode) { el.removeEventListener('mouseenter', ()=> { el.classList.add(value) }); el.removeEventListener('mouseleave', ()=> { el.classList.remove(value) }) } ``` Perhaps this is still not the best because it's still an anonymous function see: dev.to/em4nl/…
Its great to see different kinds of solution on stackovervlow, but from what I have been taught, its bad practice to mix vuejs and dom manipulation like this. Since vuejs takes its own copy of the dom, it means you can run into issues with these sort of solutions.
2

In case someone is looking for something that actually works with v-for, use this:

<div v-for="(item, index) in [1,2,3,4,5,6,7,8]" @mouseenter="state.hover=index" @mouseleave="state.hover=false">
  <p :class="{ 'yourClass': state.hover == index }">Hello World</p>
</div>

The above answers will trigger all of your elements inside a v-for loop at the same time.

Comments

1

Add the following to the div you want to animate on hover :

@mouseover="isHovering = item.id" @mouseout="isHovering = false" :class="isHovering == item.id ? 'slower animated pulse' : ''"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.