cssvue.jshoverrating

CSS and Vue: Style other elements if hover on one for Ratings


I wanna keep it simple. I am about to code a 5 star rating logic.

So I have this array of 5 possible ratings:

const ratingsArray = [
  {
    name: 'rating1',
    ratingValue: 20,
    isEnabled: ref(false)
  },
  {
    name: 'rating2',
    ratingValue: 40,
    isEnabled: ref(false)
  },
  {
    name: 'rating3',
    ratingValue: 60,
    isEnabled: ref(false)
  },
  {
    name: 'rating4',
    ratingValue: 80,
    isEnabled: ref(false)
  },
  {
    name: 'rating5',
    ratingValue: 100,
    isEnabled: ref(false)
  },
]

And I have this template. It maps through the array and gives me a circle for every entry:

The class 'ratingBoxChecked' is the class which fills the dot with a color.

<template>
  <span>
    <div 
      v-for="(rating, index) in ratingsArray" 
      :key="rating.name" 
      @click="updateRating(index, rating.ratingValue)" 
      :class="[rating.isEnabled.value ? 'ratingBoxChecked' : 'ratingBox']">
    </div>
  </span>
</template>

How can I hover on one dot and the other ones before that to are hovered and have the class 'ratingBoxChecked', too?

For example, I want to hover on the 3rd dot and the first 2 must be enabled too! Like a standard rating hover.

Thank you Guys so much!


Solution

  • Ignoring the "click" case for this example (but can be easily stitched together) - see my StackBlitz for a running version.

    Code

    <script setup>
    import { ref } from 'vue';
    
    const ratings = [
      {
        name: 'rating1',
        value: 20,
      },
      {
        name: 'rating2',
        value: 40,
      },
      {
        name: 'rating3',
        value: 60,
      },
    ];
    
    // When hovering, this ref will be changed to the index of the rating you hover over
    const hoveredRating = ref(-1);
    // Function to check if styles/text/... should be changed
    const shouldRatingShow = (index) => hoveredRating.value >= index;
    
    // Note: This is a naive implementation. You can also check it based on names (e.g. find the index of a rating based on the name and take all before) and so on and so on
    </script>
    
    <template>
      <span>
        <!-- div containing the mouseenter and mouseleave event to change the ref. PS: If you want to make it clickable, this should be a button!! -->
        <div
          v-for="(rating, index) in ratings"
          :key="rating.name"
          @mouseenter="hoveredRating = index"
          @mouseleave="hoveredRating = -1"
        >
          {{ rating.name }}
          {{ shouldRatingShow(index) ? 'on' : 'off' }}
        </div>
      </span>
    
      <!-- Debug output -->
      {{ hoveredRating }}
    </template>