Skip to content

Instantly share code, notes, and snippets.

@keithchu
Created January 9, 2012 18:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save keithchu/1584147 to your computer and use it in GitHub Desktop.
Save keithchu/1584147 to your computer and use it in GitHub Desktop.
Star Rating Using HTML <input type="range">
/**
* Star Rating Using HTML <input type="range">
*/
html {
min-height: 100%;
-webkit-tap-highlight-color: rgba(0,0,0,0);
tap-highlight-color: rgba(0,0,0,0);
}
body {
background-color: rgb(48,83,109);
background-image: -webkit-gradient(linear, from(rgb(48,83,109)), to(rgb(93,156,198))); /* Safari 4+, Chrome */
background-image: -webkit-linear-gradient(rgb(48,83,109), rgb(93,156,198)); /* Safari 5.1+, Chrome 10+ */
background-image: -moz-linear-gradient(rgb(48,83,109), rgb(93,156,198)); /* FF3.6+ */
background-image: -ms-linear-gradient(rgb(48,83,109), rgb(93,156,198)); /* IE10 */
background-image: -o-linear-gradient(rgb(48,83,109), rgb(93,156,198)); /* Opera 11.10+ */
background-image: linear-gradient(rgb(48,83,109), rgb(93,156,198));
background-repeat: no-repeat;
font-family: sans-serif;
height: 100%;
padding: 60px 0;
text-align: center;
}
a, a:link, a:visited {
color: rgba(80,0,0,.8);
text-decoration: none;
}
address {
color: #999;
font-size: .8em;
font-style: normal;
}
footer {
background-color: #f9f9f9;
border-top: 1px dashed rgba(200,200,200,.5);
color: rgba(150,150,150,.8);
font-size: .7em;
margin-top: 40px;
padding: 12px 15px;
}
footer ul {
margin: 0;
padding: 0;
}
footer li {
list-style: none;
}
header {
border-top: 5px solid #999;
padding: 4px 15px 35px;
text-align: left;
}
h1 {
color: #333;
font-size: 1em;
margin-bottom: 1px;
}
section {
padding: 0 30px;
position: relative;
text-align: center;
}
section ol {
display: block !important; /* Allows for CSS off scenario, JS turns it off, CSS turns it on */
font-size: 9px;
list-style: none;
margin: 0; padding: 0;
position: absolute;
top: 0; left: 132px;
width: 80px;
z-index: 1;
white-space: no-wrap;
}
section ol li {
display: inline-block;
line-height: 18px;
text-align: center;
width: 16px;
}
sup {
font-size: 75%;
line-height: 0;
position: relative; top: -.5em;
vertical-align: baseline;
}
input[name="rating"] {
background-color: transparent;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAzFBMVEX////PpQPdsAPitAPnuAPnuAPXqwPywQTYrAPhswPesQPitAPgswPltgPjtQPZrQPbrwPmtwPcrwPhswPargPhtAPWqwPVqgPjtAPargPWqgPmtwPltwPbrwPargPSqAPZrQPktgPXrAPXqwPWqwPWqwPltgPXrAPdsAPnuAP/ywT1wwT+ywTzwgTgsgPktgP2xATywQT8yAT3xQTRpgPjtQPSqAPuvgTesQPargPzwQT9ygTsvATdsAPwvwTitAPxwATRpwPtvQThswNb7PFZAAAAKnRSTlMAANaUBhL1/e+nx5ixSIDu4RfboOWc/f2E5/gjPt/p/uxq/vb+91L01AwBa43mAAABYElEQVR4XqXWtXbEMBBG4ZUMi9kgM4PthTDz+79TTpxtRs3VjFX5Nn/h4jvTcv8vS2cfLe2bDSTDhgObC80Gtu+/FhsNLH/8rDYamH++OjAOdL33K3m7qtp5vuR9oh7I/Pj1vayKoirL78deavgHydpNUb/p+obtH8xt1QvT4x3rT0zGfwN3XWcd2H0oRqPic886kO5fTzqdycttZhw4fDoaODc4eRsaB3qnZ+7vX55fGAf6l65+ad82ID0IW+1B2GoPwlZ7ELbag7DVHoSt9iBstQcGH6QH0ocoEaQHwodIHYQHwodIEoQHwofIAeGB8CFyQHggfIgcEB4IHyIHhAfCB4sH1OgBNXpAjR5QowfU6AE1ekCNHlDjfUCN9wE13gfUeB9Q431AjfcBNd4H1HgfUKMH2gYPuMEDbvCAGzzgBg+4wQNu8IAbPOAGD7jBA27wgBs84AYPuMEDbvCA+xccDJFDMV9c6wAAAABJRU5ErkJggg%3D%3D),
url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAlCAYAAABLRGl/AAACDElEQVR42t2Ty0pbURSGA2sTEBw5cSwdOPINxHFBcOAwIBQcxaFPcAaCLyBSQdCJRtSqMamK1GtbvEKImHhvTTWKYLw0elQwWf2XOYGj5uyY46yDD9berPWd7H/veAzD0OED9bqeYoIgGHArqAAPwATlbgTNgC0+uRHM2ATTOoEfdNkYAevg0SaQet2ix9brE0GZteASaQf08srSbxi8Ag1OGVSDmGY4AqqKhejTCPxvuYU+jSBcTEAgZRuYF2xrE5TpBLVW4wNote1LbQIG9TpBO9gANaBQwCugUydoAl7N6yTpcRC4w8PM7+H/EKypd+GJ9JEjp1NUAbyaHr3gT5g6jr6S4VqwO0Kne6OUcCU4maQPGwPE0QAx6sqSBYkQ9eIInAiBMHWXLNj5QhfpBcV/AepzRwG+EDgYo3genDu5NUS3GMxk1xQLUsuesD9Ke/leCfnpqmRxOE58/k3x1Zzi2++5QbaQWvaE1IziX0ESluSKn11ZfIgydz9lqDDmD8WxQcochamtYAbJCfoYH6R7SAoO4whmcpLqtCHKL8FxXgnwoFjyKnoLeDi/L2ZzQ+lFIVdfzytGwGdagYSCx5N9XFZ8PEG8PUyXgtSZVcXRfmIJ3VGAxhZ8hbeGKXsYomB+X2oEnMVtyVEMR4FcDRrvEGYj1q8ChvgGkk1HAQL8jJ9Yrvl7e6XHvvcPj4KZs8FIFxIAAAAASUVORK5CYII%3D);
/* background-image: url("css/images/ratings-stars-r.png"),
url("css/images/ratings.png");
*/
background-position: 0 15px, 0 0;
background-repeat: no-repeat, repeat-x;
position: relative; top: -1px;
vertical-align: top;
width: 80px;
z-index: 2;
-webkit-appearance: none;
}
input[name="rating"]::-webkit-slider-thumb {
background-color: rgba(255,203,4,.33); /* Highlight the selected item for the "Images Off" scenario */
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAlCAYAAABLRGl/AAACDElEQVR42t2Ty0pbURSGA2sTEBw5cSwdOPINxHFBcOAwIBQcxaFPcAaCLyBSQdCJRtSqMamK1GtbvEKImHhvTTWKYLw0elQwWf2XOYGj5uyY46yDD9berPWd7H/veAzD0OED9bqeYoIgGHArqAAPwATlbgTNgC0+uRHM2ATTOoEfdNkYAevg0SaQet2ix9brE0GZteASaQf08srSbxi8Ag1OGVSDmGY4AqqKhejTCPxvuYU+jSBcTEAgZRuYF2xrE5TpBLVW4wNote1LbQIG9TpBO9gANaBQwCugUydoAl7N6yTpcRC4w8PM7+H/EKypd+GJ9JEjp1NUAbyaHr3gT5g6jr6S4VqwO0Kne6OUcCU4maQPGwPE0QAx6sqSBYkQ9eIInAiBMHWXLNj5QhfpBcV/AepzRwG+EDgYo3genDu5NUS3GMxk1xQLUsuesD9Ke/leCfnpqmRxOE58/k3x1Zzi2++5QbaQWvaE1IziX0ESluSKn11ZfIgydz9lqDDmD8WxQcochamtYAbJCfoYH6R7SAoO4whmcpLqtCHKL8FxXgnwoFjyKnoLeDi/L2ZzQ+lFIVdfzytGwGdagYSCx5N9XFZ8PEG8PUyXgtSZVcXRfmIJ3VGAxhZ8hbeGKXsYomB+X2oEnMVtyVEMR4FcDRrvEGYj1q8ChvgGkk1HAQL8jJ9Yrvl7e6XHvvcPj4KZs8FIFxIAAAAASUVORK5CYII%3D);
background-position: 0 -22px;
background-repeat: repeat-x;
border: none;
height: 16px;
width: 16px;
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
/*-webkit-mask-image: url("css/images/ratings_mask.png"); /* Masks out the highlight in the "Images On" scenario */
-webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAFNJREFUeNrMkzEOACAIA+//n66TixEQ0MShW3uBJkUSlgABcj3PADMcQT4ArMaCmgDr5EjbDrLhbYmZ8H1A+wWzqArAey0EZMblrvFEAwAA//8DAJvcGgMy1wjYAAAAAElFTkSuQmCC);
-webkit-appearance: none;
}
input[name="rating"].default::-webkit-slider-thumb {
background: none;
}
output {
border: 1px solid #999;
display: inline-block;
font-size: .8em;
height: 18px;
line-height: 1.4;
margin-left: 3px;
text-align: center;
vertical-align: bottom;
width: 18px;
}
#wrapper {
background-color: #fff;
border: 1px solid #7d7d7d;
box-shadow: 0 0 15px rgba(0,0,0,.3);
display: inline-block;
text-align: left;
}
<div id="wrapper">
<header>
<h1>Star Rating Using &lt;input&nbsp;type=range&gt;</h1>
<address class="author">by <a href="http://twitter.com/catharsis" rel="author">Keith Chu</a></address>
</header>
<section>
<label for="rating">Rating: </label>
<select data-type="range" name="rating">
<option disabled selected>Select one</option>
<option>1 star</option>
<option>2 stars</option>
<option>3 stars</option>
<option>4 stars</option>
<option>5 stars</option>
</select>
<!--<input type="range" name="rating" step="1" min="1" value="1" max="5" />-->
<!--<output for="rating">0</output>--> <!-- append to input via JS -->
</section>
<footer>
<ul>
<li><sup>1</sup><a href="https://github.com/Modernizr/Modernizr/blob/master/modernizr.js#L797">Modernizr</a> &mdash; range input test</li>
<li><sup>2</sup><a href="http://remysharp.com/2011/07/18/input-range-polyfill/">Remy Sharp</a> &mdash; range input polyfill</li>
</ul>
</footer>
</div>
<!-- Javascript at the bottom for fast page loading -->
<!-- Without adding any elements, and given JS on:
1. Images off (fallback) (added OL - sk)
2. CSS off (fallback) (Hid OL - sk)
3. Images/CSS off (fallback) (If CSS is off, images are off since all images are linked through CSS - sk)
-->
<script>
/* Test if input[type="range"] supported (via Modernizr) [footnote 1] */
var bool,
output = document.createElement("output"),
range = document.createElement("input"),
smile = ":)";
range.setAttribute("type", "range");
if (bool = range.type !== "text") {
range.value = smile;
range.style.cssText = "position:absolute;visibility:hidden;";
if (range.style.WebkitAppearance !== undefined ) {
document.body.appendChild(range);
bool = range.offsetHeight !== 0; // Mobile android web browser has false positive, so must check the height to see if the widget is actually there.
document.body.removeChild(range);
}
} else {
bool = range.value == smile;
}
if (bool) { initRange(); } // input[type="range"] is supported
function initRange() {
var listItemContent, listItem,
selects = document.getElementsByTagName("select"),
list = document.createElement("ol"),
i, j;
output.setAttribute("for","rating");
output.value = 0;
range.value = range.style.cssText = ""; // reset range from support testing
/* input["range"] polyfill (via @remysharp) [footnote 2] */
for (i = selects.length; i--;) {
if (selects[i].getAttribute("data-type") == "range") { (function(select) {
/* Create an ordered list based on the select element for use in the "images off" scenario*/
for (j = 0; j < select.length; j++) {
listItemContent = Number(select.options[j].value.substr(0,1));
if (listItemContent) {
listItem = document.createElement("li");
listItem.innerHTML = listItemContent.toString();
list.appendChild(listItem);
}
}
select.parentNode.appendChild(list);
/* Turn off display and show with CSS to account for CSS off scenario */
list.style.display = "none";
/* Replace Select element with Input Type = Range */
select = select.parentNode.replaceChild(range, select);
range.name = "rating";
range.value = range.step = range.min = 1;
range.max = 5;
})(selects[i]);}
}
/* end polyfill */
range.className = "default";
range.parentNode.appendChild(output);
bindEvent(range, "touchstart", function(e) { ratingSwap(this,e); });
bindEvent(range, "mousedown", function() { ratingSwap(this); });
bindEvent(range, "change", function() { ratingSwap(this); });
/* bindEvent(range, "click", function(e) { ratingSwap(this,e); }); */
}
/**
* @param el The element to be bound.
* @param eventName The event to attach to the element.
* @param eventHandler The function to trigger on event activation.
**/
function bindEvent(el, eventName, eventHandler) {
if (el.addEventListener) { el.addEventListener(eventName, eventHandler, false); }
else if (el.attachEvent) { el.attachEvent("on" + eventName, eventHandler); }
else { alert("Event binding failed due to lack of support for addEventListener/attachEvent"); }
}
/**
* @param el The input[type="range"] element.
* @param e The interaction event.
**/
function ratingSwap(el, e) {
var areaClicked = 0,
currentStep = 0,
areaStep = 16, /* step width of range (i.e. star width) */
areaSteps = 5, /* number of steps in range (i.e. # of stars) */
i;
if (el.className == "default") {
el.className = "";
};
/* Triggers on click (1) on smartphone tap, (2) on click of current star on desktop */
if (e) {
/**
* Get click position respective to the input
* position = (click) - (relatively positioned parent location) - (input location)
**/
areaClicked = e.pageX - e.target.parentNode.offsetLeft - e.target.offsetLeft;
/* Map click positions to input values; assign values */
for (i = areaSteps; i--;) {
if (Math.abs(areaClicked) <= (i * areaStep)) {
el.value = i;
break;
}
}
}
/* Get the correct background-position for the star sprite */
currentStep = ((el.value - 2) * -(areaStep) - 1) + "px";
/* Set the correct background-position for the star sprite */
el.style.backgroundPosition = "0 " + currentStep + ", 0 0";
/* Update the output value */
output.innerHTML = el.value;
}
</script>
{"view":"split-vertical","fontsize":"100","seethrough":"","prefixfree":"1","page":"html"}
@keithchu
Copy link
Author

keithchu commented Sep 7, 2012

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment