1

I'm trying to implement a star rating on VisualForce, where a user hovers over a star and the star will highlight. I'm looking at the example here: CSS Star Rater .

The things I'm having trouble with is how can I get this into VF:

<span class="star-rating">
   <input type="radio" name="rating" value="1"><i></i>
   <input type="radio" name="rating" value="2"><i></i>
   <input type="radio" name="rating" value="3"><i></i>
   <input type="radio" name="rating" value="4"><i></i>
   <input type="radio" name="rating" value="5"><i></i>
</span>

and highlighting the star.

Here is the CSS file that I have:

.star-rating{
  font-size:0;
  white-space:nowrap;
  display:inline-block;
  width:250px;
  height:50px;
  overflow:hidden;
  position:relative;
  background:
      url('data:image/svg+xml;utf-8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="20px" height="20px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve"><polygon fill="#DDDDDD" points="10,0 13.09,6.583 20,7.639 15,12.764 16.18,20 10,16.583 3.82,20 5,12.764 0,7.639 6.91,6.583 "/></svg>');
  background-size: contain;
}

.star-rating input{ 
   -moz-appearance:none;
   -webkit-appearance:none;
   opacity: 0;
   display:inline-block;
   width: 20%;
   height: 100%; 
   margin:0;
   padding:0;
   z-index: 2;
   position: relative;
   &:hover + i,
   &:checked + i{
     opacity:1;    
   }
}

.star-rating i{
   opacity: 0;
   position: absolute;
   left: 0;
   top: 0;
   height: 100%;
   width: 20%;
   z-index: 1;
   background: 
      url('data:image/svg+xml;utf-8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="20px" height="20px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve"><polygon fill="#FFDF88" points="10,0 13.09,6.583 20,7.639 15,12.764 16.18,20 10,16.583 3.82,20 5,12.764 0,7.639 6.91,6.583 "/></svg>');
   background-size: contain;
 }

.star-rating i ~ i{
  width: 40%;
}
.star-rating i ~ i ~ i{
  width: 60%;
}
.star-rating i ~ i ~ i ~ i{
  width: 80%;
}
.star-rating i ~ i ~ i ~ i ~ i{
  width: 100%;
}

So far, I have this VisualForce code:

<apex:page docType="html-5.0" >
<apex:stylesheet value="{!URLFOR($Resource.FeedbackStarsStyling)}"/>
<apex:outputPanel styleClass="star-rating">
    <apex:form >
         <apex:selectRadio layout="lineDirection">
            <apex:selectOption itemValue="1"></apex:selectOption>
             <i></i>
            <apex:selectOption itemValue="2"></apex:selectOption>
             <i></i>
            <apex:selectOption itemValue="3"></apex:selectOption>
             <i></i>
            <apex:selectOption itemValue="4"></apex:selectOption>
             <i></i>
            <apex:selectOption itemValue="5"></apex:selectOption>
             <i></i>
        </apex:selectRadio>
    </apex:form>    
</apex:outputPanel>  

What are the changes can I make? Or do I need to approach this entirely differently because VisualForce can't do this stuff?

Thanks!

2 Answers 2

5

Note: I created a working version and put it in to a public gist, which also demonstrates the rating text updating when a value is selected.

Here's a demo of this working in Visualforce:

Rating Demo


We can get this to work, but you have to get a bit more creative, because the framework adds extra markup to our code. There's a few hurdles you have here.

First, the CSS used is actually LESS, which means it's meant to be pre-processed (which CodePen does for you). You have to expand the &:hover i and &:checked i elements.

.star-rating input:hover + i,
.star-rating input:checked + i{
     opacity:1;    
   }

This takes us to our next problem. You see, non apex:selectOption and apex:selectOptions elements end up being all coalesced together. When you look at the source code, you end up with something like this:

<form id="j_id0:j_id6" name="j_id0:j_id6" method="post" action="/apex/fivestars" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="j_id0:j_id6" value="j_id0:j_id6">

             <i></i>
             <i></i>
             <i></i>
             <i></i>
             <i></i><fieldset style="border: none;"><table role="presentation">
    <tbody><tr>
<td>
<input type="radio" name="j_id0:j_id6:j_id7" id="j_id0:j_id6:j_id7:0" value="1"><label for="j_id0:j_id6:j_id7:0"></label></td>
<td>
<input type="radio" name="j_id0:j_id6:j_id7" id="j_id0:j_id6:j_id7:1" value="2"><label for="j_id0:j_id6:j_id7:1"></label></td>
<td>
<input type="radio" name="j_id0:j_id6:j_id7" id="j_id0:j_id6:j_id7:2" value="3"><label for="j_id0:j_id6:j_id7:2"></label></td>
<td>
<input type="radio" name="j_id0:j_id6:j_id7" id="j_id0:j_id6:j_id7:3" value="4"><label for="j_id0:j_id6:j_id7:3"></label></td>
<td>
<input type="radio" name="j_id0:j_id6:j_id7" id="j_id0:j_id6:j_id7:4" value="5"><label for="j_id0:j_id6:j_id7:4"></label></td>
    </tr>
</tbody></table></fieldset><div id="j_id0:j_id6:j_id18"></div>
</form>

As you can see, the <i> elements end up getting put next to each other, and the remainder is inside a table. The CSS just won't work as is.

However, as you can see, we now have something to work with; we can actually just make the stars using the labels.

Now, this is where things get really messy. We need to start positioning everything and doing all sorts of extra CSS fuss.

So, after we tinker around with the CSS, selecting the fieldset, td elements, and labels, we end up with the following CSS. I also had to tinker with the z-index and width values to get everything working correctly, because the elements are nested differently.

.star-rating fieldset {
  font-size:0;
  white-space:nowrap;
  display:inline-block;
  width:250px;
  height:50px;
  overflow:hidden;
  position:relative;
  background:
      url('data:image/svg+xml;utf-8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="20px" height="20px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve"><polygon fill="#DDDDDD" points="10,0 13.09,6.583 20,7.639 15,12.764 16.18,20 10,16.583 3.82,20 5,12.764 0,7.639 6.91,6.583 "/></svg>');
  background-size: contain;
}

.star-rating input { 
   -moz-appearance:none;
   -webkit-appearance:none;
   opacity: 0;
   display:inline-block;
   width: 100%;
   height: 100%; 
   margin:0;
   padding:0;
   z-index: 2;
   position: relative;
}

.star-rating input:hover + label,
.star-rating input:checked + label {
     opacity:1;    
   }

.star-rating label {
   opacity: 0;
   position: absolute;
   left: 0;
   top: 0;
   height: 100%;
   width: 20%;
   z-index: 4;
   background: 
      url('data:image/svg+xml;utf-8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="20px" height="20px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve"><polygon fill="#FFDF88" points="10,0 13.09,6.583 20,7.639 15,12.764 16.18,20 10,16.583 3.82,20 5,12.764 0,7.639 6.91,6.583 "/></svg>');
   background-size: contain;
 }

.star-rating td ~ td label {
  width: 40%;
  z-index: 3;  
}
.star-rating td ~ td ~ td label {
  width: 60%;
  z-index: 2;
}
.star-rating td ~ td ~ td ~ td label {
  z-index: 1;
  width: 80%;
}
.star-rating td ~ td ~ td ~ td ~ td label {
  z-index: 0;
  width: 100%;
}

And, finally, we can display our page:

<apex:outputPanel styleClass="star-rating">
    <apex:form >
         <apex:selectRadio layout="lineDirection">
            <apex:selectOption itemValue="1"></apex:selectOption>
            <apex:selectOption itemValue="2"></apex:selectOption>
            <apex:selectOption itemValue="3"></apex:selectOption>
            <apex:selectOption itemValue="4"></apex:selectOption>
            <apex:selectOption itemValue="5"></apex:selectOption>
        </apex:selectRadio>
    </apex:form>    
</apex:outputPanel> 
6
  • This is really good! I was seeing that weird output and I was stuck at how to target those elements. It's working on Chrome but, it doesn't seem like it's working on Firefox.
    – akcorp2003
    Commented Nov 10, 2016 at 20:26
  • Never mind that. I found out that you need to convert the SVG to base64. UTF-8 ruins some browsers. Here's the link: rog.ie/blog/css-star-rater
    – akcorp2003
    Commented Nov 10, 2016 at 21:02
  • @akcorp2003 do you have more info about how you converted it to base64? I'm having the same issue. Commented Jun 25, 2018 at 18:01
  • @toadgeek url('data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDIwIDIwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48cG9seWdvbiBmaWxsPSIjRkZERjg4IiBwb2ludHM9IjEwLDAgMTMuMDksNi41ODMgMjAsNy42MzkgMTUsMTIuNzY0IDE2LjE4LDIwIDEwLDE2LjU4MyAzLjgyLDIwIDUsMTIuNzY0IDAsNy42MzkgNi45MSw2LjU4MyAiLz48L3N2Zz4=');
    – sfdcfox
    Commented Jun 25, 2018 at 18:10
  • @toadgeek Which I acquired by putting the following JavaScript into my browser's console: btoa(`<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="20px" height="20px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve"><polygon fill="#FFDF88" points="10,0 13.09,6.583 20,7.639 15,12.764 16.18,20 10,16.583 3.82,20 5,12.764 0,7.639 6.91,6.583 "/></svg>`)
    – sfdcfox
    Commented Jun 25, 2018 at 18:11
2

There's a problem with the following CSS code on Firefox:

background: 
  url('data:image/svg+xml;utf-8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="20px" height="20px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve"><polygon fill="#FFDF88" points="10,0 13.09,6.583 20,7.639 15,12.764 16.18,20 10,16.583 3.82,20 5,12.764 0,7.639 6.91,6.583 "/></svg>');
background-size: contain;

It is not valid for data URIs to contain # characters like the CSS has, and for this reason you must escape them as %23

The unescaped # character is reserved to indicate the start of a fragment identifier. Firefox is quite right to indicate a parsing error.

Therefore, please use the following CSS to fix the problem:

   .star-rating fieldset {
    font-size:0;
    white-space:nowrap;
    display:inline-block;
    width:250px;
    height:50px;
    overflow:hidden;
    position:relative;
    background:
        url('data:image/svg+xml;utf-8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="20px" height="20px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve"><polygon fill="%23DDDDDD" points="10,0 13.09,6.583 20,7.639 15,12.764 16.18,20 10,16.583 3.82,20 5,12.764 0,7.639 6.91,6.583 "/></svg>');
    background-size: contain;
}

.star-rating input { 
    -moz-appearance:none;
    -webkit-appearance:none;
    opacity: 0;
    display:inline-block;
    width: 100%;
    height: 100%; 
    margin:0;
    padding:0;
    z-index: 2;
    position: relative;
}

.star-rating input:hover + label,
.star-rating input:checked + label {
    opacity:1;    
}

.star-rating label {
    opacity: 0;
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    width: 20%;
    z-index: 4;
    background: 
        url('data:image/svg+xml;utf-8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="20px" height="20px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve"><polygon fill="%23FFDF88" points="10,0 13.09,6.583 20,7.639 15,12.764 16.18,20 10,16.583 3.82,20 5,12.764 0,7.639 6.91,6.583 "/></svg>');
    background-size: contain;
}

.star-rating td ~ td label {
    width: 40%;
    z-index: 3;  
}
.star-rating td ~ td ~ td label {
    width: 60%;
    z-index: 2;
}
.star-rating td ~ td ~ td ~ td label {
    z-index: 1;
    width: 80%;
}
.star-rating td ~ td ~ td ~ td ~ td label {
    z-index: 0;
    width: 100%;
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .