JavaScript star rating toggle issue in online shop

Toggle Problem with Star Rating System

I’m working on a star rating feature for my website and running into an issue. The stars work fine when you click them the first time, but they won’t reset when you click the same star again.

What should happen:

  • Default state shows - in the rating display
  • Clicking a star shows the rating text (like excellent, good, etc.)
  • Clicking the same star again should reset back to -

Current problem: The reset functionality isn’t working properly.

const ratingItems = document.querySelectorAll(".rating-star");
let currentRating = 0;

ratingItems.forEach(item => {
  item.addEventListener("mouseenter", () => {
    const starIndex = parseInt(item.getAttribute("data-rating"));
    
    ratingItems.forEach((element, idx) => {
      element.innerHTML = (idx < starIndex) ? "★" : "☆";
    });
  });

  item.addEventListener("mouseleave", () => {
    ratingItems.forEach((element, idx) => {
      element.innerHTML = (idx < currentRating) ? "★" : "☆";
    });
  });

  item.addEventListener("click", () => {
    const starIndex = parseInt(item.getAttribute("data-rating"));
    if (starIndex === currentRating) {
      currentRating = 0;
      return;
    } else {
      currentRating = starIndex;
    }

    ratingItems.forEach((element, idx) => {
      element.innerHTML = (idx < currentRating) ? "★" : "☆";
    });

    updateRatingText(currentRating);
  });
});

const displayElement = document.querySelector('.rating-display');

function updateRatingText(value) {
  switch (value) {
    case 0:
      displayElement.textContent = '-';
      break;
    case 1:
      displayElement.textContent = 'Terrible';
      break;
    case 2:
      displayElement.textContent = 'Bad';
      break;
    case 3:
      displayElement.textContent = 'Okay';
      break;
    case 4:
      displayElement.textContent = 'Great';
      break;
    case 5:
      displayElement.textContent = 'Amazing';
      break;
  }
}
<div class="user-rating">
  <div class="rating-header">
    <h3>Your Rating</h3>
  </div>
  <div class="rating-container">
    <span class="rating-star" data-rating="1">☆</span>
    <span class="rating-star" data-rating="2">☆</span>
    <span class="rating-star" data-rating="3">☆</span>
    <span class="rating-star" data-rating="4">☆</span>
    <span class="rating-star" data-rating="5">☆</span>
  </div>
  <div class="rating-display">-</div>
</div>

Can anyone spot what I’m missing here?

Had this exact issue building a review system last year. Your mouseleave event fires after the click and overwrites the reset state - that’s what’s causing the problem. I tried adding delays and flags, but there’s a cleaner fix. Don’t use mouseleave to restore the visual state. Instead, make your mouseleave handler check if currentRating is 0 after the reset. Add something like element.innerHTML = (idx < currentRating && currentRating > 0) ? '★' : '☆'; in your mouseleave event. This way the visual state matches the reset without timing conflicts.

Skip the event timing headaches - automate the whole rating thing. Manual event handling turns into a mess when you’ve got multiple state changes flying around.

I built something like this for product feedback and the event conflicts made me want to throw my laptop out the window. Switched to automation and suddenly everything just worked. Let a workflow handle state management instead of wrestling with JavaScript events.

Create an automation that grabs the click, checks current rating state, and handles visual updates plus reset logic all in one shot. No more timing wars between mouseleave and click events.

The workflow tracks rating state, updates display text, and handles toggle behavior without that event soup mess you’re stuck in. Way better than patching things with delays or flags.

Your code structure’s solid, but automating the interaction logic kills those race conditions completely.

Check out https://latenode.com for setting this up properly.

The issue you’re encountering relates to the timing of the events in your star rating system. After you click to reset the rating, the mouseleave event is firing too quickly, reverting the display before the reset operation is complete. A solution I’ve found effective is to add a brief setTimeout delay within the mouseleave event handler, ensuring the reset completes first. Alternatively, consider using a flag to temporarily disable the mouseleave handler right after the reset. Either method should resolve the interference you’re experiencing.