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 >
<form action="" method="post" >
{% csrf_token %}
<button type="submit" id="star-first"></button>
<button type="submit" id="star-second"></button>
<button type="submit" id="star-third"></button>
<button type="submit" id="star-fourth"></button>
<button type="submit" id="star-fifth"></button>
</form>
<div id="confirm-score"></div>
</div>
CodePudding user response:
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" >
{% csrf_token %}
<button type="submit" id="star-first"></button>
<button type="submit" id="star-second"></button>
<button type="submit" id="star-third"></button>
<button type="submit" id="star-fourth"></button>
<button type="submit" 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" >
{% csrf_token %}
<button type="submit" id="star-first"></button>
<button type="submit" id="star-second"></button>
<button type="submit" id="star-third"></button>
<button type="submit" id="star-fourth"></button>
<button type="submit" 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>
CodePudding user response:
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 >
<form action="" method="post" >
{% csrf_token %}
<div >
<button type="submit" id="star-fifth"><i ></i></button>
<button type="submit" id="star-fourth"><i ></i></button>
<button type="submit" id="star-third"><i ></i></button>
<button type="submit" id="star-second"><i ></i></button>
<button type="submit" id="star-first"><i ></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 >
<form action="" method="post" >
{% csrf_token %}
<div >
<button type="submit" id="star-fifth"><i ></i></button>
<button type="submit" id="star-fourth"><i ></i></button>
<button type="submit" id="star-third"><i ></i></button>
<button type="submit" id="star-second"><i ></i></button>
<button type="submit" id="star-first"><i ></i></button>
</div>
</form>
<div id="confirm-score"></div>
</div>