skip to Main Content

I’m trying to make a star rating system for my Django project using JS and jQuery.
I want to change the color of stars when the mouse is on each star.

I don’t want the star color to persist on unhover.

I followed this tutorial on YouTube.
In this video it seems to work.
JavaScript method preferred

(function($) {
  "use strict";

  $(document).ready(function() {

    const starone = document.getElementById('star-first')
    const startwo = document.getElementById('star-second')
    const starthird = document.getElementById('star-third')
    const starfourth = document.getElementById('star-fourth')
    const starfifth = document.getElementById('star-fifth')

    const form = document.querySelector('.rate-form')
    const confirmBox = document.getElementById('confirm-score')
    const csrf = document.getElementsByName('csrfmiddlewaretoken')

    const handleStarSelect = (size) => {
      const children = form.children
      for (let i = 0; i < children.length; i++) {
        if (i <= size) {
          children[i].classList.add('checked')
        } else {
          children[i].classList.remove('checked')
        }
      }
    }
    // handleStarSelect(2)

    const handleSelect = (selection) => {
      switch (selection) {
        case 'star-first':
          {
            // starone.classList.add('checked')
            // startwo.classList.remove('checked')
            // starthird.classList.remove('checked')
            // starfourth.classList.remove('checked')
            // starfifth.classList.remove('checked')
            handleStarSelect(1)
            return
          }

        case 'star-second':
          {
            handleStarSelect(2)
            return
          }
        case 'star-third':
          {
            handleStarSelect(3)
            return
          }
        case 'star-fourth':
          {
            handleStarSelect(4)
            return
          }
        case 'star-fifth':
          {
            handleStarSelect(5)
            return
          }

      }

    }

    const arr = [starone, startwo, starthird, starfourth, starfifth]

    arr.forEach(item => item.addEventListener('mouseover', (event) => {
      handleSelect(event.target.id)
    }))

  });
})(jQuery);
.checked {
  color: #f2994a;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer"
/>
<div class="user-rating">
  <form action="" method="post" class="rate-form">
    {% csrf_token %}
    <button type="submit" class="fa fa-star fa-lg" id="star-first"></button>
    <button type="submit" class="fa fa-star fa-lg" id="star-second"></button>
    <button type="submit" class="fa fa-star fa-lg" id="star-third"></button>
    <button type="submit" class="fa fa-star fa-lg" id="star-fourth"></button>
    <button type="submit" class="fa fa-star fa-lg" id="star-fifth"></button>
  </form>
  <div id="confirm-score"></div>
</div>

2

Answers


  1. You can achieve the same result with CSS alone. Don’t forget to add keyboard focus events.

    EDIT: Going over your question again I saw that you didn’t want a CSS solution. A CSS solution is often better than a JavaScript solution as some users disable JavaScript for security and CSS can make some things simpler. I’m going to also add a vanilla JavaScript answer for you but I recommend only using JavaScript where absolutely needed.

    .stars {
      display: inline flex;
      flex-flow: row-reverse;
    }
    
    .star {
      background-color: initial;
      border: initial;
    }
    
    .star:hover,
    .star:focus,
    .star:hover~.star,
    .star:focus~.star {
      cursor: pointer;
      color: #f2994a;
    }
    
    .star:focus {
      outline: 2px solid #f2994a;
    }
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer"
    />
    <div class="user-rating">
      <form action="" method="post" class="rate-form">
        {% csrf_token %}
        <div class="stars">
          <button type="submit" class="star" id="star-fifth"><i class="fa fa-star fa-lg"></i></button>
          <button type="submit" class="star" id="star-fourth"><i class="fa fa-star fa-lg"></i></button>
          <button type="submit" class="star" id="star-third"><i class="fa fa-star fa-lg"></i></button>
          <button type="submit" class="star" id="star-second"><i class="fa fa-star fa-lg"></i></button>
          <button type="submit" class="star" id="star-first"><i class="fa fa-star fa-lg"></i></button>
        </div>
      </form>
      <div id="confirm-score"></div>
    </div>

    JavaScript solution:

    document.querySelectorAll('.star').forEach((star) => {
      star.addEventListener('mouseenter', addRating)
      star.addEventListener('focus', addRating)
      star.addEventListener('mouseleave', removeRating)
      star.addEventListener('blur', removeRating)
    })
    
    function addRating(event) {
      applyRating(event.target, "add")
    }
    
    function removeRating(event) {
      applyRating(event.target, "remove")
    }
    
    function applyRating(element, action) {
      document.querySelectorAll(`#${element.id}~.star`).forEach((prevStar) => prevStar.classList[action]('checked'))
      element.classList[action]('checked')
    }
    .stars {
      display: inline flex;
      flex-flow: row-reverse;
    }
    
    .star {
      background-color: initial;
      border: initial;
    }
    
    .checked {
      color: #f2994a;
    }
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer"
    />
    <div class="user-rating">
      <form action="" method="post" class="rate-form">
        {% csrf_token %}
        <div class="stars">
          <button type="submit" class="star" id="star-fifth"><i class="fa fa-star fa-lg"></i></button>
          <button type="submit" class="star" id="star-fourth"><i class="fa fa-star fa-lg"></i></button>
          <button type="submit" class="star" id="star-third"><i class="fa fa-star fa-lg"></i></button>
          <button type="submit" class="star" id="star-second"><i class="fa fa-star fa-lg"></i></button>
          <button type="submit" class="star" id="star-first"><i class="fa fa-star fa-lg"></i></button>
        </div>
      </form>
      <div id="confirm-score"></div>
    </div>
    Login or Signup to reply.
  2. i just checked your code its working normally but the only problem i saw is that it is selecting next one while hovering in current this is because for loop is running from 0 so handleStarSelect(0) means first element

    take a look at corrected indexes example

        const starone = document.getElementById('star-first')
        const startwo = document.getElementById('star-second')
        const starthird = document.getElementById('star-third')
        const starfourth = document.getElementById('star-fourth')
        const starfifth = document.getElementById('star-fifth')
        
        const form = document.querySelector('.rate-form')
        const confirmBox = document.getElementById('confirm-score')
        const csrf = document.getElementsByName('csrfmiddlewaretoken')
        
        const handleStarSelect = (size) => {
          const children = form.children
          for (let i = 0; i < children.length; i++) {
            if (i <= size) {
              children[i].classList.add('checked')
            } else {
              children[i].classList.remove('checked')
            }
          }
        }
        // handleStarSelect(2)
        
        const handleSelect = (selection) => {
          switch (selection) {
            case 'star-first':
              {
                handleStarSelect(0)
                return
              }
        
            case 'star-second':
              {
                handleStarSelect(1)
                return
              }
            case 'star-third':
              {
                handleStarSelect(2)
                return
              }
            case 'star-fourth':
              {
                handleStarSelect(3)
                return
              }
            case 'star-fifth':
              {
                handleStarSelect(4)
                return
              }
        
          }
        
        }
        
        const arr = [starone, startwo, starthird, starfourth, starfifth]
        
        arr.forEach(item => item.addEventListener('mouseover', (event) => {
          handleSelect(event.target.id)
        }))
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/all.min.css" rel="stylesheet">
        <title>Experiment</title>
    </head>
    
    <body>
        <style>
            button {background:transparent;border: none;}
            .checked {
                color: #f2994a;
            }
        </style>
        <form action="" method="post" class="rate-form">
            {% csrf_token %}
            <button type="submit" class="fa fa-star fa-lg" id="star-first"></button>
            <button type="submit" class="fa fa-star fa-lg" id="star-second"></button>
            <button type="submit" class="fa fa-star fa-lg" id="star-third"></button>
            <button type="submit" class="fa fa-star fa-lg" id="star-fourth"></button>
            <button type="submit" class="fa fa-star fa-lg" id="star-fifth"></button>
        </form>
    </body>
    <!-- <link rel="stylesheet" href="./styles.css"> -->
    <!-- <script src="./main.js"></script> -->
    <!-- <script src="https://cdn.tailwindcss.com"></script> -->
    
    </html>

    i also wanted to suggest my code that i write to perform same behavior if you want to you can use this as well

    function manageRating() {
        const btns = [...document.querySelectorAll(".rate-form > button")]
    
        function reset() {btns.forEach((each) => { each.classList.remove("checked") })}
            
        function rate(number) {
            reset()
            for (let c = 0; c <= number; c++) {
                btns[c].classList.add("checked")
            }
        }
    
        btns.forEach((each) => {
            each.addEventListener("mouseenter", e => {
                let index = btns.indexOf(e.target)
                rate(index)
            })
        })
        btns[0].parentElement.addEventListener("mouseleave",reset)
    }
    manageRating()
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/all.min.css" rel="stylesheet">
        <title>Experiment</title>
    </head>
    
    <body>
        <style>
            button {background:transparent;border: none;}
            .checked {
                color: #f2994a;
            }
        </style>
        <form action="" method="post" class="rate-form">
            {% csrf_token %}
            <button type="submit" class="fa fa-star fa-lg" id="star-first"></button>
            <button type="submit" class="fa fa-star fa-lg" id="star-second"></button>
            <button type="submit" class="fa fa-star fa-lg" id="star-third"></button>
            <button type="submit" class="fa fa-star fa-lg" id="star-fourth"></button>
            <button type="submit" class="fa fa-star fa-lg" id="star-fifth"></button>
        </form>
    </body>
    <!-- <link rel="stylesheet" href="./styles.css"> -->
    <!-- <script src="./main.js"></script> -->
    <!-- <script src="https://cdn.tailwindcss.com"></script> -->
    
    </html>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search