How to toggle between classes using jquery - javascript

I have a rating star in my page, and I'm trying to toggle back and forth between "fa fa-star" class and "fa fa-star checked" class I have read about this here: Javascript/Jquery to change class onclick?
And this is my implementation (it didn't work)
$('.fa fa-star').click(function() { $(this).toggleClass('fa fa-star'); $(this).toggleClass('fa fa-star checked'); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<span onclick="rating" class="fa fa-star"></span>
<span onclick="rating" class="fa fa-star checked"></span>
<span onclick="rating" class="fa fa-star checked"></span>
<span onclick="rating" class="fa fa-star checked"></span>
<span onclick="rating" class="fa fa-star"></span>
</div>

You simply need to toggle the checked class for a simplified rating.
$('.stars i').click(function() {
$('.stars i.checked').removeClass('checked');
for (let i = 0; i < $(this).index() + 2; i++) {
$('.stars i:nth-of-type(' + i + ')').addClass('checked');
}
});
.stars i {
color: #ccc;
cursor: pointer;
}
.stars i.checked {
color: #dfd022;
}
<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.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<div class="stars">
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
</div>
If you need the option of being able to convert stars back to '0', you can use the following option:
$('.stars i').click(function() {
if($(this).index() === 0 && $('.stars i.checked').length === 1){
$('.stars i.checked').removeClass('checked');
} else {
$('.stars i.checked').removeClass('checked');
for (let i = 0; i < $(this).index() + 2; i++) {
$('.stars i:nth-of-type(' + i + ')').addClass('checked');
}
}
});
.stars i {
color: #ccc;
cursor: pointer;
}
.stars i.checked {
color: #dfd022;
}
<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.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<div class="stars">
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
</div>

maybe that...
document.querySelectorAll('i.fa-star')
.forEach( (star,indx,arr) =>
{
star.onclick =_=>
{
let chk = star.classList.toggle('checked')
arr.forEach((s,i) =>
s.classList.toggle('checked', i<indx ? true: i===indx ? chk : false))
}
})
body {
background : darkslategrey;
font-size : 24px;
}
i.fa-solid.fa-star {
color : lightgray;
cursor : pointer;
}
i.fa-solid.fa-star.checked {
color : gold;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" />
<div>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
</div>
If you want the same as prettyInPink's answer :
document.querySelectorAll('i.fa-star')
.forEach( (star,indx,arr) => star.onclick =_=>
arr.forEach((s,i) => s.classList.toggle('checked', i<=indx )))
body {
background : darkslategrey;
font-size : 24px;
}
i.fa-solid.fa-star {
color : lightgray;
cursor : pointer;
}
i.fa-solid.fa-star.checked {
color : gold;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" />
<div>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
<i class="fa-solid fa-star"></i>
</div>

Related

when I try to translate the element through js, it doesn't work

All the elements with class em, needs to be shown once at the time on the basis of the rating.
I made the emoji contaneir as big as the icons, so with overflow hidden only one can be shown.
The problem is that when i try to modify the transform property, it doesn't work.
const starsEl = document.querySelectorAll(".fa-star");
const ratingcEl = document.querySelectorAll(".em");
console.log(ratingcEl)
starsEl.forEach((starsEl, index) => {
starsEl.addEventListener("click", () => {
console.log('click')
updateRating(index);
});
});
function updateRating(index) {
starsEl.forEach((starsEl, idx) => {
if (idx <= index) {
starsEl.classList.add("active");
} else {
starsEl.classList.remove("active");
}
});
ratingcEl.forEach((ratingcEl) => {
console.log(index)
ratingcEl.style.transform = `translateX(- ${ 50 * index}px)`;
});
}
.emoji-container {
position: absolute;
top: 20%;
left: 50%;
transform: translateX(-50%);
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
overflow: hidden;
}
.emoji-container i {
margin: 1px;
transform: translateX(-200px);
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<body>
<div class="feedback-container">
<div class="emoji-container">
<i class="em fa-regular fa-face-angry fa-3x"></i>
<i class="em fa-regular fa-face-frown fa-3x"></i>
<i class="em fa-regular fa-face-meh fa-3x"></i>
<i class="em fa-regular fa-face-smile fa-3x"></i>
<i class="em fa-regular fa-face-laugh fa-3x"></i>
</div>
<div class="rating-container">
<i class="fa-solid fa-star fa-2x "></i>
<i class="fa-solid fa-star fa-2x "></i>
<i class="fa-solid fa-star fa-2x "></i>
<i class="fa-solid fa-star fa-2x "></i>
<i class="fa-solid fa-star fa-2x "></i>
</div>
</div>
<script src="emojii.js"></script>
</body>
I want the icons to translate left, in order to show the right face for the rating.
This can be greatly simplified. Removed all position/transform/display rules from CSS. They bring unnecessary complexity for such a simple task. Also added gold color for stars with active class and started with 5 active star.
const starsEl = document.querySelectorAll(".fa-star");
const emoji = document.getElementById("emoji");
const emojis = ["angry", "frown", "meh", "smile", "laugh"]; // just the changing parts
starsEl.forEach((starsEl, index) => {
starsEl.addEventListener("click", () => {
updateRating(index);
});
});
function updateRating(index) {
starsEl.forEach((starsEl, idx) => {
if (idx <= index) {
starsEl.classList.add("active");
} else {
starsEl.classList.remove("active");
}
});
emoji.classList = ["em fa-regular fa-face-" + emojis[index] + " fa-3x"];
}
#rating-container > .active {
color: gold;
}
#emoji-container {
text-align: center;
margin-top: 2rem;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<body>
<div id="feedback-container">
<div id="rating-container">
<i class="fa-solid fa-star fa-2x active"></i>
<i class="fa-solid fa-star fa-2x active"></i>
<i class="fa-solid fa-star fa-2x active"></i>
<i class="fa-solid fa-star fa-2x active"></i>
<i class="fa-solid fa-star fa-2x active"></i>
</div>
<div id="emoji-container">
<i id="emoji" class="em fa-regular fa-face-laugh fa-3x"></i>
</div>
</div>
<script src="emojii.js"></script>
</body>

How to make a function in jQuery last longer?

When I click on one of the buttons with "arrow" class the whole nav is disappearing. (other than that, the jquery code is working well). But why it is happening? I want the whole nav to disappear when I click on the ".btn" again and not when I click on the button.arrow...
$('.btn').click(function() {
$(this).toggleClass("click");
$('.sidebar').toggleClass("show");
});
nav.sidebar {
display: none;
}
nav.sidebar.show {
display: block;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<main class="btn">
<span class="fas fa-cog"></span>
<nav class="sidebar">
<button class="arrow">X <i class="fas fa-arrow-circle-up"></i></button>
<button class="arrow">X <i class="fas fa-arrow-circle-down"></i></button>
<button class="arrow">Y <i class="fas fa-arrow-circle-up"></i></button>
<button class="arrow">Y <i class="fas fa-arrow-circle-down"></i></button>
</nav>
</main>
The issue is that the .arrow elements are nested within the .btn element, so a click on an arrow is also a click on the button.
To avoid this, you could stop the event that is triggered by arrows from propagating to the button.
$('.btn').click(function() {
$(this).toggleClass("click");
$('.sidebar').toggleClass("show");
});
$('.arrow').click(function(event) {
event.stopPropagation();
// Do other work that should happen
// because an arrow got clicked
console.log("You clicked on an arrow element.");
});
nav.sidebar {
display: none;
}
nav.sidebar.show {
display: block;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<main class="btn">
<span class="fas fa-cog"></span>
<nav class="sidebar">
<button class="arrow">X <i class="fas fa-arrow-circle-up"></i></button>
<button class="arrow">X <i class="fas fa-arrow-circle-down"></i></button>
<button class="arrow">Y <i class="fas fa-arrow-circle-up"></i></button>
<button class="arrow">Y <i class="fas fa-arrow-circle-down"></i></button>
</nav>
</main>
You want the .btn to toggle ONLY if the click event occured directly on the cog icon...
$('.btn').click(function(event) {
if($(event.target).is(".fa-cog")){
$(this).toggleClass("click");
$('.sidebar').toggleClass("show");
}
});
nav.sidebar {
display: none;
}
nav.sidebar.show {
display: block;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<main class="btn">
<span class="fas fa-cog"></span>
<nav class="sidebar">
<button class="arrow">X <i class="fas fa-arrow-circle-up"></i></button>
<button class="arrow">X <i class="fas fa-arrow-circle-down"></i></button>
<button class="arrow">Y <i class="fas fa-arrow-circle-up"></i></button>
<button class="arrow">Y <i class="fas fa-arrow-circle-down"></i></button>
</nav>
</main>
I could also be as simple as this:
$('.fa-cog').click(function() {
$('.sidebar').toggleClass("show");
});
nav.sidebar {
display: none;
}
nav.sidebar.show {
display: block;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<main class="btn">
<span class="fas fa-cog"></span>
<nav class="sidebar">
<button class="arrow">X <i class="fas fa-arrow-circle-up"></i></button>
<button class="arrow">X <i class="fas fa-arrow-circle-down"></i></button>
<button class="arrow">Y <i class="fas fa-arrow-circle-up"></i></button>
<button class="arrow">Y <i class="fas fa-arrow-circle-down"></i></button>
</nav>
</main>

I want to create a responsive star rating in javascript

This is my HTML
<div id="starRatings">
<span class="fa fa-star checked"></span>
<span class="fa fa-star "></span>
<span class="fa fa-star "></span>
<span class="fa fa-star "></span>
<span class="fa fa-star "></span>
This is my Javascript
const starRating = $("#starRatings").children();
for (let i = 0; i < starRating.length; i++) {
starRating[i].addEventListener("mouseover", function() {
for (let j = 1; j <= starRating.length; j++) {
$(starRating[j]).removeClass("checked");
}
for (let j = 1; j> i; j--) {
$(starRating[j]).addClass("checked");
console.log(starRating[j])
}
});
starRating[i].addEventListener("mouseleave", function() {
for (let j = 1; j <= starRating.length; j++) {
$(starRating[j]).addClass("checked");
console.log(j)
}
for (let j = 1; j> i; j--) {
$(starRating[j]).removeClass("checked");
console.log(j)
}
});
starRating[i].addEventListener("click", function() {
for (let j = 1; j <= starRating.length; j++) {
$(starRating[j]).addClass("checked");
}
for (let j = 1; j> i; j--) {
$(starRating[j]).removeClass("checked");
}
});
}
The class checked simply gives the stars an orange colour and I want one to be orange at all times. I want the others to turn orange on mouse enter and back to grey on mouse leave AND when the user actually wants to click on a number of stars, they remain orange, even after mouse leave, for this im guessing I need an if statement somewhere.
I realise it should be simple and I've got wayyy too many for loops, but im losing my mind!
Can anyone help?
I can offer you an example with jquery
https://jsfiddle.net/mp4oze3f/
CSS
.et-app-sr {
border: 0;
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
word-wrap: normal !important
}
.et-app-rating-stars {
height: 4em;
display: flex
}
.et-app-rating-star {
background: none;
border: none;
box-shadow: none;
font-size: 3.4em;
cursor: pointer;
}
.et-app-rating-star .selected {
display: none;
color: #edff71
}
.et-app-rating-star.select i, .et-app-rating-star.highlight i {
display: none
}
.et-app-rating-star.select .selected, .et-app-rating-star.highlight .selected {
display: block
}
.et-app-rating-star:focus {
outline: 1px solid #000
}
JS (jQuery)
$('[data-star]').on('click', function(e) {
e.preventDefault();
$(this).addClass('select');
$(this).prevAll('[data-star]').addClass('select');
$(this).nextAll('[data-star]').removeClass('select');
$(this).parent().find('input').val($(this).data('star'));
});
$('[data-star]').on('mouseover', function(e) {
e.preventDefault();
$(this).addClass('highlight');
$(this).prevAll('[data-star]').addClass('highlight');
$(this).nextAll('[data-star]').removeClass('highlight');
});
$('[data-star]').on('mouseout', function(e) {
e.preventDefault();
$(this).removeClass('highlight');
$(this).prevAll('[data-star]').removeClass('highlight');
});
HTML
<div class="et-app-rating-stars">
<input type="hidden">
<button class="et-app-rating-star" data-star="1">
<i class="fa fa-star-o" aria-hidden="true"></i>
<i class="fa fa-star selected" aria-hidden="true"></i>
<span class="et-app-sr">Rating: 1 star</span>
</button>
<button class="et-app-rating-star" data-star="2">
<i class="fa fa-star-o" aria-hidden="true"></i>
<i class="fa fa-star selected" aria-hidden="true"></i>
<span class="et-app-sr">Rating: 2 star</span>
</button>
<button class="et-app-rating-star" data-star="3">
<i class="fa fa-star-o" aria-hidden="true"></i>
<i class="fa fa-star selected" aria-hidden="true"></i>
<span class="et-app-sr">Rating: 3 star</span>
</button>
<button class="et-app-rating-star" data-star="4">
<i class="fa fa-star-o" aria-hidden="true"></i>
<i class="fa fa-star selected" aria-hidden="true"></i>
<span class="et-app-sr">Rating: 4 star</span>
</button>
<button class="et-app-rating-star" data-star="5">
<i class="fa fa-star-o" aria-hidden="true"></i>
<i class="fa fa-star selected" aria-hidden="true"></i>
<span class="et-app-sr">Rating: 5 star</span>
</button>
</div>
I used more than you need, I change icons, you can look at the logic and do something like this

'click' event only fires on first click and not second

I have five elements and want something to happend when I click one of them. My code works fine when clicking for first time, but the event never hits on second click. Any idea why?
$("#ratingModal").find(".star.clickable").on("click", function () {
console.log("click");
var $wrap = $(this).closest(".stars");
var count = parseInt($(this).data("star"));
$.each($wrap.find(".star"), function ($index, $itm) {
let current = parseInt($($itm).data("star"));
$($itm).removeClass("fas");
$($itm).removeClass("far");
console.log(current);
if (current <= count) {
$($itm).addClass("fas");
console.log("fas");
}
else {
$($itm).addClass("far");
console.log("far");
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<div id="ratingModal">
<div class="body">
<div class="score-wrap">
<span class="label">Score: </span>
<div class="stars">
<i class="fas fa-star clickable star star-1" title="1" data-star="1"></i>
<i class="far fa-star clickable star star-2" title="2" data-star="2"></i>
<i class="far fa-star clickable star star-3" title="3" data-star="3"></i>
<i class="far fa-star clickable star star-4" title="4" data-star="4"></i>
<i class="far fa-star clickable star star-5" title="5" data-star="5"></i>
</div>
</div>
</div>
</div>
Try Using off().on('...') function
$("#ratingModal").find(".star.clickable").off().on("click", function () {
console.log("click");
var $wrap = $(this).closest(".stars");
var count = parseInt($(this).data("star"));
$.each($wrap.find(".star"), function ($index, $itm) {
let current = parseInt($($itm).data("star"));
$($itm).removeClass("fas");
$($itm).removeClass("far");
console.log(current);
if (current <= count) {
$($itm).addClass("fas");
console.log("fas");
}
else {
$($itm).addClass("far");
console.log("far");
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<div id="ratingModal">
<div class="body">
<div class="score-wrap">
<span class="label">Score: </span>
<div class="stars">
<i class="fas fa-star clickable star star-1" title="1" data-star="1"></i>
<i class="far fa-star clickable star star-2" title="2" data-star="2"></i>
<i class="far fa-star clickable star star-3" title="3" data-star="3"></i>
<i class="far fa-star clickable star star-4" title="4" data-star="4"></i>
<i class="far fa-star clickable star star-5" title="5" data-star="5"></i>
</div>
</div>
</div>
</div>

Multiple fa-heart like count increment and decrement

I need to increment the count of a fa-heart value on click and decrement on the second click.
Problem is I have multiple fa-heart in the same page, so unable to increment/decrement on the clicked fa-heart.
Here is my fiddle below
https://jsfiddle.net/mehbub/d9e2qotg/1/
(function() {
const heart = document.getElementById('heart');
heart.addEventListener('click', function() {
heart.classList.toggle('red');
});
})();
$(document).on('click', ".notliked", function() {
var $this = $(this);
$this.removeClass('notliked');
$this.addClass('liked')
$count = $('.likes-count');
$count.text(function(idx, txt) {
return (+txt == 0) ? 0 : (+txt - 1);
heart.classList.toggle('grey');
});
});
$(document).on('click', ".liked", function() {
var $this = $(this);
$this.removeClass('liked');
$this.addClass('notliked');
$count = $('.likes-count');
$count.text(function(idx, txt) {
return +txt + 1;
heart.classList.toggle('red');
});
});
$count.text(function(idx, txt) {
// convert text to number and increment by one
return +txt + 1;
});
#heart {
color: grey;
font-size: 20px;
}
#heart.red {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<p>
robert stephen</p><i class="fa fa-heart liked heart" id="heart" value="1" ></i>
<span class="likes-count"> 100 </span><br>
<p>
James Camroon</p>
<i class="fa fa-heart liked heart" id="heart" value="1" ></i>
<span class="likes-count"> 101 </span><br>
<p>
John Wick</p>
<i class="fa fa-heart liked heart" id="heart" value="1" ></i>
<span class="likes-count"> 37 </span><br>
<p>
James Bond</p>
<i class="fa fa-heart liked heart" id="heart" value="1" ></i>
<span class="likes-count"> 22 </span><br>
<p>
Avengers</p>
<i class="fa fa-heart liked heart" id="heart" value="1" ></i>
<span class="likes-count"> 90 </span>
I need individual click increment/decrement for each fa-heart to color change to red if increment, or grey if the decrement
Thanks
You should remove the duplicate id heart or just remove it since you've already a class heart then attach the click to this common class and toggle the classes liked/notliked, you could use those classes as a rules in your CSS so no need for red/grey classes, something like :
$(document).on('click', ".heart", function() {
var count = $(this).next('span');
$(this).toggleClass('notliked liked');
if ($(this).hasClass('liked')) {
count.text(Number(count.text()) + 1);
} else {
count.text(Number(count.text()) - 1);
}
});
.heart.notliked {
color: grey;
font-size: 20px;
}
.heart.liked {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<p>robert stephen</p>
<i class="fa fa-heart liked heart" value="1"></i>
<span class="likes-count"> 100 </span><br>
<p>James Camroon</p>
<i class="fa fa-heart liked heart" value="1"></i>
<span class="likes-count"> 101 </span><br>
<p>John Wick</p>
<i class="fa fa-heart liked heart" value="1"></i>
<span class="likes-count"> 37 </span><br>
<p>James Bond</p>
<i class="fa fa-heart liked heart" value="1"></i>
<span class="likes-count"> 22 </span><br>
<p>Avengers</p>
<i class="fa fa-heart liked heart" value="1"></i>
<span class="likes-count"> 90 </span>

Categories