skip to Main Content

I have this code to show stars from a decimal.

.rating {
  width: 120px;
  height: 24px;
  position: relative;
  background-color: gray;
}

.rating progress.rating-bg {
  -webkit-appearance: none;
  -moz-appearence: none;
  appearance: none;
  border: none;
  display: inline-block;
  height: 24px;
  width: 100%;
  color: orange;
}

.rating progress.rating-bg::-webkit-progress-value {
  background-color: orange;
}

.rating progress.rating-bg::-moz-progress-bar {
  background-color: orange;
}

.rating svg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
<svg style="display:none;">
  <defs>
    <symbol id="fivestars">
      <path d="M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z M0 0 h24 v24 h-24 v-24" fill="white" fill-rule="evenodd"/>
      <path d="M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z M0 0 h24 v24 h-24 v-24" fill="white" fill-rule="evenodd" transform="translate(24)"/>
      <path d="M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z M0 0 h24 v24 h-24 v-24" fill="white" fill-rule="evenodd" transform="translate(48)"/>
      <path d="M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z M0 0 h24 v24 h-24 v-24" fill="white" fill-rule="evenodd" transform="translate(72)"/>
      <path d="M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z M0 0 h24 v24 h-24 v-24" fill="white" fill-rule="evenodd"  transform="translate(96)"/>
    </symbol>
  </defs>
</svg>
<div class="rating">
  <progress class="rating-bg" value="4.9" max="5"></progress>
  <svg><use xlink:href="#fivestars"/></svg>
</div>

https://codepen.io/ppolp/pen/mdQyezW

It works but I’m trying to figure out how to remove the white rectange bg from the stars. Anyone able to point me in the right direction?

Thanks.

2

Answers


  1. FYI, you can wrap it all in a native JavaScript (JSWC) Web Component:

    <star-rating stars=5 rating="3.5"
                 bgcolor="green" nocolor="grey" color="gold"></star-rating>
    

    It is way easier if you create all SVG client-side, using a Custom Element (supported in all modern Browsers):

    • copied only the d-path from a star icon
    • Edited the d-path in https://yqnn.github.io/svg-path-editor/ to a 100×100
      viewBox/grid
    • made it an inverse star by prepending M0 0h100v100h-100v-100 to the path
    • Created a new SVG file in a 0 0 N 100 viewBox to fit all stars.. see below
    • Added a background rectangle setting gold color rating
    • Used inverse stars, each at an x-offset
    • added rectangles covering all half-stars
    • set inline events onclick and onmouseover on every "half-star"
    <star-rating stars=5 rating="3.5"
                 bgcolor="green" nocolor="grey" color="gold"></star-rating>
    <star-rating stars=7 rating="50%"
                 bgcolor="rebeccapurple" nocolor="beige" color="goldenrod"></star-rating>
    <script>
      document.addEventListener("click", (evt) => console.log(evt.target.getAttribute("n")))
    
      customElements.define("star-rating", class extends HTMLElement {
        set rating( rate ) {
          if (!String(rate).includes("%")) rate = Number(rate) / this.stars * 100 + "%";
          this.querySelector("#rating").setAttribute("width", rate);
        }
        connectedCallback() {
          let { bgcolor, stars, nocolor, color, rating } = this.attributes;
          this.stars = ~~stars.value || 5;
          this.innerHTML = 
            `<svg viewBox="0 0 ${this.stars*100} 100" style="cursor:pointer;width:300px">`
          + `<rect width="100%" height="100" fill="${nocolor.value}"/>`
          + `<rect id="rating"  height="100" fill="${color.value}"  />`
            + Array(  this.stars     ).fill()
                   .map((i, n) => `<path fill="${bgcolor.value}" d="M${ n*100 } 0h102v100h-102v-100m91 42a6 6 90 00-4-10l-22-1a1 1 90 01-1 0l-8-21a6 6 90 00-11 0l-8 21a1 1 90 01-1 1l-22 1a6 6 90 00-4 10l18 14a1 1 90 010 1l-6 22a6 6 90 008 6l19-13a1 1 90 011 0l19 13a6 6 90 006 0a6 6 90 002-6l-6-22a1 1 90 010-1z"/>`)
                   .join("")
            + Array(  this.stars * 2 ).fill()
                   .map((i, n) => `<rect x="${ n*50 }" n="${n}" opacity="0" width="50" height="100"`
                      + ` onclick="dispatchEvent(new Event('click'))" `
                      + ` onmouseover="this.closest('star-rating').rating = ${(n+1)/2}"/>`)
                  .join("") 
          + "</svg>";
    
          this.rating = rating.value;
        }
      });
    </script>
    Login or Signup to reply.
  2. Good idea to use the <progress> element. Instead of laying the progress bar and the SVG on top of each other you can use an SVG as a mask on the progress bar. Here I defined a star mask that will be a mask for just one star and masked off five rectangles in another mask. This mask can then be applied to the progress bar.

    I took the freedom to construct the star instead of using a path. When using masks like in this example it doesn’t matter.

    document.forms.form01.range.addEventListener('change', e => {
      document.getElementById('rating').setAttribute('value', e.target.value);
    });
    .rating progress.rating-bg {
      -webkit-appearance: none;
      -moz-appearence: none;
      appearance: none;
      border: none;
      display: inline-block;
      height: 24px;
      width: 120px;
      color: orange;
      background-color: #ddd;
      mask: url(#m1);
    }
    
    .rating progress.rating-bg::-webkit-progress-value {
      background-color: orange;
    }
    
    .rating progress.rating-bg::-moz-progress-bar {
      background-color: orange;
    }
    <svg style="display: block;" width="0" height="0" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <mask id="star">
          <rect width="24" height="24" fill="white"/>
          <g transform="translate(12 12)">
            <rect id="r1" width="48" height="24" fill="black" transform="translate(0 7) rotate(24) skewX(-42)"/>
            <use href="#r1" transform="rotate(72)"/>
            <use href="#r1" transform="rotate(144)"/>
            <use href="#r1" transform="rotate(216)"/>
            <use href="#r1" transform="rotate(288)"/>
          </g>
        </mask>
        <mask id="m1">
          <rect id="r2" width="24" height="24" fill="white" mask="url(#star)"/>
          <use href="#r2" transform="translate(24 0)"/>
          <use href="#r2" transform="translate(48 0)"/>
          <use href="#r2" transform="translate(72 0)"/>
          <use href="#r2" transform="translate(96 0)"/>
        </mask>
      </defs>
    </svg>
    <div class="rating">
      <progress id="rating" class="rating-bg" value="2" max="5"></progress>
    </div>
    <form name="form01">
      <input type="range" name="range" step=".25" min="0" max="5" value="2">
    </form>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search