1

Not sure if this is doable, but is there a way you can use .length or another method to get the rating score of something which is usually displayed in svg form?

In the below HTML in each svg there's a "g fill" which determines what is outputted.

  • "var(--review-star-active)" = 1 star
  • "url('#57_rating-half-star')" = half star
  • "var(--review-star-inactive)" = 0 star

Is there a way to use .length here to determine what the rating is? This should be 3.5 stars in this case.

document.querySelector(".productRating").insertAdjacentHTML("afterend", "Review is: " + document.querySelectorAll('.productStar').length);
<div class="productRating">
  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">
    <g fill="var(--review-star-active)">
    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>
    </g></svg>
  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">
  <g fill="var(--review-star-active)">
    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>
    </g></svg>
  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">
  <g fill="var(--review-star-active)">
    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>
    </g></svg>
  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">
  <g fill="url('#57_rating-half-star')">
    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>
    </g></svg>
  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">
  <g fill="var(--review-star-inactive)">
    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>
    </g></svg>
</div>
leek1234
  • 470
  • 2
  • 9

3 Answers3

3

You can select the g elements directly, convert the returned NodeList to an array (here using spread syntax) and filter() it by the fill attribute; once for the full stars and again for the half stars.

const stars = [...document.querySelectorAll('.productStar g')];

const fullCount = stars.filter(g =>
  g.getAttribute('fill') === 'var(--review-star-active)').length;

const halfCount = stars.filter(g =>
  g.getAttribute('fill').includes('half-star')).length;

console.log(fullCount + halfCount / 2);
<div class="productRating">  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">    <g fill="var(--review-star-active)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="var(--review-star-active)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="var(--review-star-active)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="url('#57_rating-half-star')">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="var(--review-star-inactive)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg></div>

ES5

var stars = [].slice.call(document.querySelectorAll('.productStar g'));

var fullCount = stars.filter(function (g) {
  return g.getAttribute('fill') === 'var(--review-star-active)';
}).length;

var halfCount = stars.filter(function (g) {
  return g.getAttribute('fill').indexOf('half-star') !== -1;
}).length;

console.log(fullCount + halfCount / 2);
<div class="productRating">  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">    <g fill="var(--review-star-active)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="var(--review-star-active)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="var(--review-star-active)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="url('#57_rating-half-star')">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="var(--review-star-inactive)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg></div>

Example iterating over each productRating

var ratings = document.querySelectorAll('.productRating');

ratings.forEach(function (rating) {
  var stars = [].slice.call(rating.querySelectorAll('.productStar g'));

  var fullCount = stars.filter(function (g) {
    return g.getAttribute('fill') === 'var(--review-star-active)';
  }).length;

  var halfCount = stars.filter(function (g) {
    return g.getAttribute('fill').indexOf('half-star') !== -1;
  }).length;

  rating.insertAdjacentHTML("afterend", "Review is: " + (fullCount + halfCount / 2));
});
<div class="productRating">  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">    <g fill="var(--review-star-active)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="var(--review-star-active)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="var(--review-star-active)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="url('#57_rating-half-star')">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="var(--review-star-inactive)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg></div>

<div class="productRating">  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">    <g fill="var(--review-star-active)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="var(--review-star-active)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="url('#57_rating-half-star')">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="var(--review-star-inactive)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg>  <svg class="productStar" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">  <g fill="var(--review-star-inactive)">    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>    </g></svg></div>
pilchard
  • 12,414
  • 5
  • 11
  • 23
  • This looks to work exactly what i need, thank you! One thing though how would you write this in ES5? The online tool we use doesn't support ES6 so not sure how to rewrite this for it to work correctly. – leek1234 Jul 26 '21 at 11:19
  • Sorry i've got one last question, how would i do this for more then one product rating? I've tried creating a forLoop but it just ends up adding each of the reviews together instead of individually. – leek1234 Jul 26 '21 at 11:55
  • 1
    I just added a new snippet. You can query all the `productRatings` and then iterate over them querying `productStar g` on each rating. `productRatings.forEach(rating => {rating.querySelectorAll('.productStar g') ...}` – pilchard Jul 26 '21 at 12:19
  • One last thing, I'm only noticing now on the main site that the half star reviews #number is unique per product. Is there a way to target this same attribute with it ends with 'half-star' i've something like the below, not sure if you know of anything that would work: var half = stars.filter(function (g) { return g.querySelectorAll('g[fill$="half-star"]'); }).length/2; – leek1234 Jul 26 '21 at 14:04
  • 1
    You can use `String.includes()` in the filter for ES6 or `String.prototype.indexOf()` for ES5. Alternatively you can query directly with a wildcard `rating.querySelectorAll('g[fill*="half-star"]')` (the fill value doesn't end with `half-star` it ends with `half-star')"`. The benefit of the `filter()` is that it only requires one DOM query, queries by Attribute Value are not very efficient, where as `filter()` has a known complexity on a small set of elements. – pilchard Jul 26 '21 at 14:13
  • 1
    Edited to use `include()` and `indexOf()` in the half-star filters. – pilchard Jul 26 '21 at 14:24
0

You could add another class to the productStar element and use that to calculate the rating

  <svg class="productStar productStar--filled" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">
  <g fill="var(--review-star-active)">
    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>
    </g></svg>
  <svg class="productStar productStar--half" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">
  <g fill="url('#57_rating-half-star')">
    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>
    </g></svg>
  <svg class="productStar productStar--empty" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 17" width="18" height="18">
  <g fill="var(--review-star-inactive)">
    <polygon points="9 13.5 3.70993273 16.2811529 4.72024568 10.3905765 0.440491353 6.21884705 6.35496636 5.35942353 9 0 11.6450336 5.35942353 17.5595086 6.21884705 13.2797543 10.3905765 14.2900673 16.2811529"></polygon>
    </g></svg>

then you can do

document.querySelector(".productRating").insertAdjacentHTML("afterend",
  "Review is: " + (
    document.querySelectorAll('g[fill="var(--review-star-active)]"').length +
    document.querySelectorAll('g[fill~="url"]').length * 0.5
  )
);
codebreach
  • 2,155
  • 17
  • 30
0
let fullStars = document.querySelectorAll('.productStar > g[fill="var(--review-star-active)"').length

You won't be able to select half stars the same way, so I'd suggest including a adding a class 'halfStars' to the 'productStar' div. This will give you:

<svg class="productStar halfStar"> ..... </svg>

let halfStars = document.querySelectorAll('.halfStar').length / 2

You could actually just put 'activeStar' and 'inactiveStar' as classes in the other svgs to make things simpler. So finally you'll be left with:

let rating = fullStars + halfStars
document.querySelector(".productRating").insertAdjacentHTML("afterend", "Review is: " + rating);
Omar Siddiqui
  • 1,625
  • 5
  • 18