Skip to main content

Overview

Reseñas Gastronómicas features an intuitive star rating system that allows reviewers to rate dishes on a scale of 1 to 10. The system provides real-time visual feedback and supports independent ratings from multiple reviewers.
The application uses a 10-star system (1-10 scale) rather than the traditional 5-star system, allowing for more granular rating precision.

StarRating Module

The StarRating module manages all rating interactions and state:
export const StarRating = {
    ratings: { gian: 0, yami: 0 },

    init() {
        document.querySelectorAll('.star-rating').forEach(rating => {
            const reviewer = rating.dataset.reviewer;
            const stars = rating.querySelectorAll('.star');

            stars.forEach((star, index) => {
                star.addEventListener('click', () => this.setRating(reviewer, index + 1));
                star.addEventListener('mouseover', () => this.highlightStars(reviewer, index + 1));
                star.addEventListener('mouseout', () => this.resetStarHighlight(reviewer));
            });
        });
    }
};

Multi-Reviewer Support

The system supports independent ratings from two reviewers (Gian and Yami):
ratings: { gian: 0, yami: 0 }
Each reviewer has:
  • Separate rating state
  • Independent star displays
  • Individual rating text display
Ratings are stored separately per reviewer, allowing each person to rate dishes independently without affecting the other’s rating.

Setting Ratings

When a user clicks on a star, the rating is set:
setRating(reviewer, rating) {
    rating = Math.max(0, Math.min(rating, 10));
    this.ratings[reviewer] = rating;
    const el = document.getElementById(`${reviewer}Rating`);
    if (el) el.textContent = `${rating}/10`;
    this.updateStarDisplay(reviewer, rating);
}
Process:
  1. Clamp rating between 0 and 10
  2. Update internal state
  3. Update text display (e.g., “8/10”)
  4. Update visual star display

Rating Validation

rating = Math.max(0, Math.min(rating, 10));
This ensures the rating is always within valid bounds:
  • Minimum: 0
  • Maximum: 10
Always validate rating values before storing them to prevent invalid data in your database.

Visual Feedback

The star rating system provides rich visual feedback through hover effects and click interactions.

Hover Effect

When a user hovers over stars, they light up to preview the rating:
highlightStars(reviewer, rating) {
    const stars = document.querySelectorAll(`.star-rating[data-reviewer="${reviewer}"] .star`);
    stars.forEach((star, index) => {
        star.classList.toggle('star-filled', index < rating);
        star.classList.toggle('star-empty', index >= rating);
    });
}
Example: Hovering over the 7th star:
  • Stars 1-7: Add star-filled class (highlighted)
  • Stars 8-10: Add star-empty class (dim)

Hover Reset

When the mouse leaves the star area, the display reverts to the current rating:
resetStarHighlight(reviewer) {
    this.updateStarDisplay(reviewer, this.ratings[reviewer]);
}
This ensures users see their current rating when not hovering.
The system uses two CSS classes to style stars:
  • star-filled: Bright, highlighted star (active/selected)
  • star-empty: Dim, unhighlighted star (inactive)
These classes are toggled dynamically based on user interaction.

Update Star Display

The core method that updates the visual appearance of stars:
updateStarDisplay(reviewer, rating) {
    const stars = document.querySelectorAll(`.star-rating[data-reviewer="${reviewer}"] .star`);
    stars.forEach((star, index) => {
        star.classList.toggle('star-filled', index < rating);
        star.classList.toggle('star-empty', index >= rating);
    });
}

Event Handling

The star rating system uses three event listeners per star:

Click Event

star.addEventListener('click', () => this.setRating(reviewer, index + 1));
Permanently sets the rating when clicked.

Mouseover Event

star.addEventListener('mouseover', () => this.highlightStars(reviewer, index + 1));
Temporarily highlights stars on hover to preview the rating.

Mouseout Event

star.addEventListener('mouseout', () => this.resetStarHighlight(reviewer));
Resets to the current saved rating when hover ends.
The index is 0-based, so we add 1 when setting the rating to convert to a 1-10 scale.

Reset Functionality

The system can reset all ratings to zero:
reset() {
    this.ratings = { gian: 0, yami: 0 };
    const g = document.getElementById('gianRating');
    const y = document.getElementById('yamiRating');
    if (g) g.textContent = `0/10`;
    if (y) y.textContent = `0/10`;
    this.updateStarDisplay('gian', 0);
    this.updateStarDisplay('yami', 0);
}
Use cases:
  • After submitting a review
  • When canceling review creation
  • When clearing the form
Always call StarRating.reset() after successfully submitting a review to clear the form for the next entry.

Integration with Form

The star rating system integrates seamlessly with the review form:
// From form.js
const reviewData = {
    reviewers: {}
};

if (gianActive) {
    reviewData.reviewers.gian = {
        rating: StarRating.ratings.gian,
        review: document.getElementById('gianReview').value
    };
}

if (yamiActive) {
    reviewData.reviewers.yami = {
        rating: StarRating.ratings.yami,
        review: document.getElementById('yamiReview').value
    };
}
Ratings are read from the StarRating module’s state when the form is submitted.

HTML Structure

The star rating system expects a specific HTML structure:
<div class="star-rating" data-reviewer="gian">
    <span class="star star-empty"></span>
    <span class="star star-empty"></span>
    <span class="star star-empty"></span>
    <span class="star star-empty"></span>
    <span class="star star-empty"></span>
    <span class="star star-empty"></span>
    <span class="star star-empty"></span>
    <span class="star star-empty"></span>
    <span class="star star-empty"></span>
    <span class="star star-empty"></span>
</div>
<div id="gianRating">0/10</div>
Key elements:
  • Container with .star-rating class
  • data-reviewer attribute to identify the reviewer
  • 10 star elements with .star class
  • Rating display element with ID {reviewer}Rating

Data Flow

1

User Interaction

User hovers over or clicks a star
2

Event Handler

Event listener captures the interaction
3

State Update

Rating state is updated in the ratings object
4

Visual Update

Stars and rating text are updated visually
5

Form Submission

Rating is read from state when form is submitted
6

Reset

After successful submission, ratings are reset to 0

Dynamic Rating Display

The system displays the current rating in text form:
const el = document.getElementById(`${reviewer}Rating`);
if (el) el.textContent = `${rating}/10`;
Examples:
  • 0 stars: “0/10”
  • 5 stars: “5/10”
  • 10 stars: “10/10”

Reviewer-Specific Selectors

The system uses data attributes for precise targeting:
const stars = document.querySelectorAll(`.star-rating[data-reviewer="${reviewer}"] .star`);
This ensures:
  • Each reviewer’s stars are controlled independently
  • No cross-contamination between reviewers
  • Clean, maintainable code
Using data-reviewer allows us to:
  1. Have multiple rating systems on the same page
  2. Target specific reviewer’s stars without complex selectors
  3. Easily identify which reviewer a rating belongs to
  4. Scale to additional reviewers if needed in the future

Accessibility Considerations

For better accessibility, consider adding:
<div class="star-rating" 
     data-reviewer="gian" 
     role="radiogroup" 
     aria-label="Rate from 1 to 10 stars">
    <span class="star" 
          role="radio" 
          aria-checked="false" 
          tabindex="0"></span>
    <!-- More stars... -->
</div>
Benefits:
  • Screen readers can announce the rating purpose
  • Keyboard navigation support
  • ARIA states communicate selection

Edge Cases

Rating of 0

A rating of 0 means “not rated yet”:
ratings: { gian: 0, yami: 0 }
All stars appear empty, and the display shows “0/10”.

Missing Elements

The code includes safety checks:
if (el) el.textContent = `${rating}/10`;
This prevents errors if the rating display element doesn’t exist.

Performance

The star rating system is highly performant:
  • Uses event delegation concepts with querySelectorAll
  • Minimal DOM manipulation (only updates changed classes)
  • No complex calculations
  • State stored in simple object

Fast Updates

Visual feedback is instant with no perceptible lag

Lightweight

Minimal memory footprint and CPU usage

Scalable

Can handle multiple rating systems on one page

No Dependencies

Pure JavaScript with no external libraries required

Best Practices

Before submitting to the database, validate that ratings are within 1-10:
if (StarRating.ratings.gian < 1 || StarRating.ratings.gian > 10) {
    alert('Please select a valid rating');
    return;
}
Always reset the star rating after successfully creating a review:
await DataStore.addReview(reviewData);
StarRating.reset();
Check if a reviewer is active before requiring their rating:
if (gianActive && StarRating.ratings.gian === 0) {
    alert('Gian must provide a rating');
    return;
}

Future Enhancements

Potential improvements to the star rating system:
  • Half-star support (e.g., 7.5 stars)
  • Keyboard navigation for accessibility
  • Touch gesture support for mobile devices
  • Animated transitions when rating changes
  • Rating explanations (1-3: Poor, 4-6: Average, 7-9: Good, 10: Excellent)

Code Reference

Key files for the star rating system:
  • starrating.js:1-54 - Complete StarRating module
  • starrating.js:17-23 - Rating setter method
  • starrating.js:25-43 - Visual feedback methods
  • form.js:38-50 - Integration with form submission

Build docs developers (and LLMs) love