0

I'm learning Javascript and as an exercise I want to take a list of text ratings and convert it into star ratings.

There are probably some easier ways and libraries to do this but I wanted to try it with my own code just so I can understand how the code works.

I've mostly stumbled my way to a nearly working solution. However, I keep getting an extra star after going through the loops.

Here is a link to fiddle with the full code... Fiddle

This is my HTML

<div class="container" id="container">
        <div class="rating" id="rating">1.2</div>
        <div class="rating" id="rating">3.2</div>
        <div class="rating" id="rating">4.8</div>
        <div class="rating" id="rating">5</div>
        <div class="rating" id="rating">2.6</div>
    </div>

this is my javascript

I'm basically getting all the ".ratings" elements from the parent "container"

const ratingsContainer = document.getElementById('container');
const ratings = ratingsContainer.getElementsByClassName('rating');

Iterating through them;

In the for loop Im fetching the innerHTML of each ratings div.

const ratingScore = parseFloat(ratings[i].innerHTML).toFixed(1);

Im converting them to decimal numbers because I have to split the rating in order to get the number of full stars needed and the number of fractional stars needed.

Im also calculating the number of empty stars needed.

let emptyStars;
  emptyStars = fullStars < 5 ? 5 - fullStars - 1 : 0;

I then create the required elements for the fullstars, the fractional stars and the empty stars through a loop for each that looks something like this...

for (let i = 0; i < fullStars; i++) {
    const newstarContainer = document.createElement('div');
    newstarContainer.className = 'starContainer';

    const starFill = document.createElement('div');
    starFill.className = 'starFill';
    const starFillImage = document.createElement('img');
    starFillImage.src = 'images/star.svg';

    const starOutline = document.createElement('div');
    starOutline.className = 'starOutline';
    const starOutlineImage = document.createElement('img');
    starOutlineImage.src = 'images/outline.svg';
    const newstarContainerFill = newstarContainer
      .appendChild(starFill)
      .appendChild(starFillImage);
    const newstarContainerOutline = newstarContainer
      .appendChild(starOutline)
      .appendChild(starOutlineImage);
    document
      .getElementById('container')
      .appendChild(newRatingsContainer)
      .appendChild(newstarContainer);
  }

I do this for the empty and fractional stars as well.
The fractional stars get a custom width style added to the star image container this cuts off the star.svg image based on the percentage width.

const starFill = document.createElement('div');
    starFill.className = 'starFill';
    starFill.style.width = `${fractionalStars}%`;

Finally I append everything to the container and remove the original text ratings.

My problem is when the text ratings number is 0 or not existing, I get an extra star. I've tried to play around with the code but I cant seem to figure out why its happening.

Can someone please help me to explain what Im doing wrong.

McDavid
  • 33
  • 1
  • 4
  • 1
    First of all, don't give each `div` the same `id="rating"`. The `id` of a `div` has to be unique. – diiN__________ Jan 06 '23 at 12:38
  • *"I've tried to play around with the code"* - More specifically, are you [using a debugger](https://stackoverflow.com/q/25385173/328193)? When you step through the code in a debugger, which operation first produces an unexpected result? What were the values used in that operation? What was the result? What result was expected? Why? – David Jan 06 '23 at 12:49
  • `for (let i = 0; i < 1; i++) {` means that the fractional stars code always runs once. You could replace this with an if statement and work out the number of empty stars differently to account for the possibility that you may not need a fractional star. – Ben Stephens Jan 06 '23 at 12:51
  • @BenStephens Thank you. I used your suggestion. Just added an if statement to check whether fractionalStars was greater than 0 and that works. Here is the updated working [Fiddle](https://jsfiddle.net/6wrkbdua/) – McDavid Jan 06 '23 at 13:30
  • There's still a bit more to do; try 2 as one of the values for the stars. – Ben Stephens Jan 06 '23 at 14:29
  • @BenStephens yeah, I've fixed that and cleaned the code up a bit to avoid repetition. This was the problem `emptyStars = fullStars < 5 ? 5 - fullStars - 1 : 0;` I was checking for fullStars instead of fractionalStars. So i changed it to `emptyStars = fractionalStarsFill > 0 ? 4 - fullStars : 5 - fullStars;` – McDavid Jan 06 '23 at 14:52

1 Answers1

0

Got a few pointers and finally came up with something that works. The first issue was getting an extra star.

That turned out to be a problem with the part of the code that fractional and empty stars.

I was running the loop like this...

for (let i = 0; i < 1; i++) {

That meant it always run at least once. This meant that when there was no fractional star to be added like in the case of an integer without a float, the loop still run and generated a star with a width of 0%. Which looked like extra empty star. I solved that by changing the loop to an if statement since it was only supposed to run once. So the code became

if (fractionalStarsFill > 0) {

The second problem was the way I was getting the number of empty stars required. I was doing it like this...

emptyStars = fullStars < 5 ? 5 - fullStars - 1 : 0;

Which is obviously wrong. I think I was tired. I changed that to

emptyStars = fractionalStarsFill > 0 ? 4 - fullStars : 5 - fullStars;

This takes into account whether there are fractional stars or not and generates the number of empty stars accordingly.

Finally, I refactored the code and moved the repeated statements into a function, and changed a few variables to more meaningful names.

Here is a Fiddle to the final solution.

McDavid
  • 33
  • 1
  • 4