Reputation: 7218
I have implemented this star rating snippet with good success in my Chrome extension popup html:
Here is my implementation:
.rating {
float: left;
unicode-bidi: bidi-override;
direction: rtl;
font-size: 28px;
margin-top: -11px;
color: #e8d04c;
margin-left: 10px;
margin-right: 10px;
}
.rating > span {
display: inline-block;
position: relative;
width: 0.7em;
cursor: pointer;
}
.rating > span:hover:before,
.rating > span:hover ~ span:before {
content: "\2605";
position: absolute;
}
<div id="divRating" class="rating">
<span id="spanRatingExcellent" title="Excellent">☆</span>
<span id="spanRatingGood" title="Good">☆</span>
<span id="spanRatingFair" title="Fair">☆</span>
<span id="spanRatingPoor" title="Poor">☆</span>
<span id="spanRatingAwful" title="Awful">☆</span>
</div>
It works fine except that this approach doesn't provide a way for the selected star to "stick". For example, if the user clicks 4 stars, when the mouse pointer leaves, I want stars 1-4 to remain highlighted. Instead, all stars unhighlight upon mouseout.
But then if the use mouse-overs again and clicks a star, I need correct (original) behavior. And again, if a star is clicked, the selected stars should remain highlighted upon mouse-out and any unselected stars (to the right) would remain unhighlighted.
A pure CSS solution would be cool, but I'm perfectly happy to use jQuery or JavaScript to achieve this.
Upvotes: 1
Views: 1736
Reputation: 5444
Here's an implementation using pure JavaScript, along with your HTML and CSS.
document.getElementById('divRating').addEventListener('click', function(event) {
if (event.target.tagName.toLowerCase() != 'span') return;
if (event.target.classList.contains('rated')) {
event.target.classList.remove('rated');
} else {
Array.prototype.forEach.call(document.getElementsByClassName('rated'), function(el) {
el.classList.remove('rated');
});
event.target.classList.add('rated');
}
});
.rating {
float: left;
unicode-bidi: bidi-override;
direction: rtl;
font-size: 28px;
margin-top: -11px;
color: #e8d04c;
margin-left: 10px;
margin-right: 10px;
}
.rating > span {
display: inline-block;
position: relative;
width: 0.7em;
cursor: pointer;
}
.rating > span:hover:before,
.rating > span:hover ~ span:before,
.rating > span.rated:before,
.rating > span.rated ~ span:before {
content: "\2605";
position: absolute;
}
<div id="divRating" class="rating">
<span id="spanRatingExcellent" title="Excellent">☆</span>
<span id="spanRatingGood" title="Good">☆</span>
<span id="spanRatingFair" title="Fair">☆</span>
<span id="spanRatingPoor" title="Poor">☆</span>
<span id="spanRatingAwful" title="Awful">☆</span>
</div>
Upvotes: 1
Reputation: 6574
If you want a pure CSS version, then Lea Verou provides that, and is linked to from the article above under the section Actual Usage.
It works by using radio buttons to hold the value of which stars have been selected.
See the JS fiddle here and the explainer here.
Here's her code pulled out (not taking credit for this solution, just pointing to it).
<fieldset class="rating">
<legend>Please rate:</legend>
<input type="radio" id="star5" name="rating" value="5" /><label for="star5" title="Rocks!">5 stars</label>
<input type="radio" id="star4" name="rating" value="4" /><label for="star4" title="Pretty good">4 stars</label>
<input type="radio" id="star3" name="rating" value="3" /><label for="star3" title="Meh">3 stars</label>
<input type="radio" id="star2" name="rating" value="2" /><label for="star2" title="Kinda bad">2 stars</label>
<input type="radio" id="star1" name="rating" value="1" /><label for="star1" title="Sucks big time">1 star</label>
</fieldset>
CSS:
.rating {
float:left;
}
/* :not(:checked) is a filter, so that browsers that don’t support :checked don’t
follow these rules. Every browser that supports :checked also supports :not(), so
it doesn’t make the test unnecessarily selective */
.rating:not(:checked) > input {
position:absolute;
top:-9999px;
clip:rect(0,0,0,0);
}
.rating:not(:checked) > label {
float:right;
width:1em;
padding:0 .1em;
overflow:hidden;
white-space:nowrap;
cursor:pointer;
font-size:200%;
line-height:1.2;
color:#ddd;
text-shadow:1px 1px #bbb, 2px 2px #666, .1em .1em .2em rgba(0,0,0,.5);
}
.rating:not(:checked) > label:before {
content: '★ ';
}
.rating > input:checked ~ label {
color: #f70;
text-shadow:1px 1px #c60, 2px 2px #940, .1em .1em .2em rgba(0,0,0,.5);
}
.rating:not(:checked) > label:hover,
.rating:not(:checked) > label:hover ~ label {
color: gold;
text-shadow:1px 1px goldenrod, 2px 2px #B57340, .1em .1em .2em rgba(0,0,0,.5);
}
.rating > input:checked + label:hover,
.rating > input:checked + label:hover ~ label,
.rating > input:checked ~ label:hover,
.rating > input:checked ~ label:hover ~ label,
.rating > label:hover ~ input:checked ~ label {
color: #ea0;
text-shadow:1px 1px goldenrod, 2px 2px #B57340, .1em .1em .2em rgba(0,0,0,.5);
}
.rating > label:active {
position:relative;
top:2px;
left:2px;
}
Upvotes: 2
Reputation: 6699
use Rate Yo jQuery star rating plugin with data attribute
$(function () {
$(".rateyo").rateYo().on("rateyo.change", function (e, data) {
var rating = data.rating;
$(this).parent().find('.result').text('rating :'+ rating);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rateYo/2.3.2/jquery.rateyo.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/rateYo/2.3.2/jquery.rateyo.min.css" rel="stylesheet"/>
<div>
<div class="rateyo" data-rateyo-rating="2.5" data-rateyo-num-stars="5"></div>
<span class='result'>0</span>
</div>
Upvotes: 1