master00
master00

Reputation: 137

Change class of element on mouseenter/mouseleave event within v-for loop in Vue.js

I'm trying to change the class of a single element within v-for loop on a mouseenter/mouseleave event but I'm confused on how to make it so that only the class on the element that is being hovered changes.

I've tried binding the class to a variable but that obviously causes all the elements in the list to change.

<template>
  <ul>
    <li class="item" v-for="item in items" @mouseenter="showInfoBar()" @mouseleave="hideInfoBar()">
        <span class="infobar" :class="{ show : infoBar }"> </span>
    </li>
  </ul>
</template>

<script>
export default {
  name: 'ItemsList',
  props: ['items'],
  data()  {
    return {
      infoBar: false
    }
  },
  methods: {
    showInfoBar() {
      this.infoBar = true
    },
    hideInfoBar() {
      this.infoBar = false
    }
  }
}
</script>

Upvotes: 3

Views: 10020

Answers (3)

ibn_Abubakre
ibn_Abubakre

Reputation: 112

You could use this instead

<template>
  <ul>
    <li class="item" v-for="(item, index) in items" :key="index" @mouseenter="infoBar = index" @mouseleave="infoBar = null">
        <span class="infobar" :class="{ show : infoBar == index }"> </span>
    </li>
  </ul>
</template>
Then for your data property,
data()  {
    return {
      infoBar: null
    }
},

Upvotes: 2

bretanac93
bretanac93

Reputation: 627

I think instead of handling this as an array, the better approach should be handling it as separated components, I mean, is better practice to leave the state of each component to each component instead of handling the state of a collection of "components" in an array, I'm basing on React to this idea, you can declare ItemsList and Item as different components, and then for each Item, a separate state, please, tell me if you understand this approach, good luck.

Edition after reading the code

I think also, once you refactor your code, instead of handling the state changing in two different methods, just declare a toggleMethod() and make a logical denial to the current value of the state you are looking at the moment.

This is better:

function toggleState() {
    this.someState = !this.someState;
}

Than this

function showState() {
    this.someState = true;
}
function hideState() {
    this.someState = false;
}

Good luck mate.

Upvotes: 0

abagshaw
abagshaw

Reputation: 6582

The problem is that you have infoBar representing the state of all the info bars, when they should be controlled independently.

Turning infoBar into an array, with each element representing the state of the info bar at that respective index should solve your problem.

<template>
  <ul>
    <li class="item" v-for="(item, index) in items" @mouseenter="showInfoBar(index)" @mouseleave="hideInfoBar(index)">
        <span class="infobar" :class="{ show : infoBar[index] }"> </span>
    </li>
  </ul>
</template>

<script>
export default {
  name: 'ItemsList',
  props: ['items'],
  data()  {
    return {
      infoBar: []
    }
  },
  mounted: function() {
    for(var i = 0; i < this.items.length; i++) {
      this.infoBar.push(false);
    }
  },
  methods: {
    showInfoBar(index) {
      this.infoBar[index] = true;
    },
    hideInfoBar(index) {
      this.infoBar[index] = false;
    }
  }
}
</script>

Upvotes: 0

Related Questions