HowTo: Star rating con riempimento parziale

Notando che molti utenti hanno dimostrato interesse nel mio precedente articolo dove ho fatto uso di SCSS e HTML per mostrare uno star rating con gestione del mezzo punteggio, ho pensato che sia giusto soddisfare le curiosità e le necessità di tutti affrontando il tema sotto un’altro aspetto: come si può dare la percezione di un punteggio che non sia limitato alle unità e alle mezze unità?

L’idea alla base era di utilizzare sempre e solo CSS e HTML, ma purtroppo l’unica soluzione che avevo era di utilizzare la funzione attr di CSS, ma attualmente è supportato esclusivamente per la proprietà content quindi con solo CSS questa volta non è possibile farlo a meno che non si abbia voglia di scrivere e gestire numerose regole CSS.

La struttura di base

Darò per assunto che hai seguito i passi per utilizzare FontAwesome, ampiamente descritto nel precedente articolo (lo riporto di seguito per semplificarti la ricerca).

La base di partenza è comunque del codice HTML. Supponiamo sia il seguente: un blocco HTML nel quale dovremo disegnare le stelle vuote e le stelle piene.

<div class="star-rating" data-rating="4.6">
</div>

Aggiungeremo quindi due elementi innestati, uno che conterrà le stelle vuote e uno che conterrà le stelle piene.

<div class="star-rating" data-rating="4.6">

  <div class="empty-stars">
  </div>
  
  <div class="full-stars">
  </div>

</div>

Infine inseriremo le nostre stelline.

<div class="star-rating" data-rating="4.6">

  <div class="empty-stars">
    <i class="far fa-star"></i>
    <i class="far fa-star"></i>
    <i class="far fa-star"></i>
    <i class="far fa-star"></i>
    <i class="far fa-star"></i>
  </div>
  
  <div class="full-stars">
    <i class="fas fa-star"></i>
    <i class="fas fa-star"></i>
    <i class="fas fa-star"></i>
    <i class="fas fa-star"></i>
    <i class="fas fa-star"></i>
  </div>

</div>

Da notare con attenzione: le stelle piene e le stelle vuote utilizzano entrambe due classi. Una è fa-star e, l’altra, nel caso delle stelle vuote è far, mentre per quelle piene è fas.

Eseguendo il codice di cui sopra in un browser, il risultato ottentuto sarebbe il seguente:

Non è tanto, ma abbiamo già un’ottima base su cui lavorare.

Certamente avrei potuto creare le stelle attraverso JavaScript, ma vuoi togliermi il piacere di scrivere del markup ripetitivo fatto di copia & incolla che non ti costringe a pensare? :-D

Scelta di stile

La prima cosa da fare è sovrapporre le stelle piene alle stelle vuote.

.star-rating {
   display: inline-block;
   position: relative;
 }
 .empty-stars {
   position: absolute;
 }

Per l’elemento con classe star-rating ho applicato la regola position: relarive, perchè in questo modo viene elevato dal DOM pur occupando ancora lo spazio in cui si trova. Infatti, se avessi optato per un posizionamento assoluto, gli elementi che lo seguono, apparirebbero sopra come se il nostro elemento non esistesse.

Il posizionamento assoluto invece viene attribuito alle stelle vuote (elemento con classe empty-stars) così da non occupare lo spazio, e consentendo alle stelle piene di collocarsi al di sopra delle stelle vuote.

Il risultato ottenuto è quello che vedete nell’esempio a seguire:

Per rendere più evidente la sovrapposizione, definiamo colori differenti per le stelle piene e le stelle vuote ed un’opacità per le stelle piene (così da vedere anche il bordo delle stelle vuote):

.star-rating {
   display: inline-block;
   position: relative;
 }
 .empty-stars {
   position: absolute;
   color: #222;
 }
 .full-stars {
   color: yellow;
   opacity: 0.8;
 }

Il nuovo risultato sarà certamente più interesante del precedente:

A questo punto definiamo un altro paio di regole che descriveremo nel dettaglio dopo, quando ti spiegherò la parte JavaScript.

.star-rating {
   display: inline-block;
   position: relative;
 }
 .empty-stars {
   position: absolute;
   color: #222;
 }
 .full-stars {
   overflow: hidden;
   white-space: nowrap;
   color: yellow;
   opacity: 0.8;
 }

Le regole che ho definito specificano che se le stelle non dovessero rientrare nella dimensione del box contenitore, non devono essere visibili; infine se il box contenitore dovesse essere più piccolo del suo contenuto, evita che ciò che è riportato dentro ritorni a capo.

Il risultato è identico al precedente ma, se notate nel secondo esempio, dove ho forzato la dimensione dell’elemento full-stars ad essere del 50%, vedremo la differenza:

Un po’ di codice JavaScript

Il titolo dell’ultima parte riporta “un po’ di codice” non a caso, ci vorrà realmente solo una manciata di codice JavaScript per mostrare un riempimento delle stelle progressivo, in base al valore specificato nell’attributo personalizzato HTML data-rating.

1. var starRatings = document.querySelectorAll('.star-rating');
2. for (var index = 0; index < starRatings.length; index++) {
3.     var starRating   = starRatings[index],
4.         fullStars    = starRating.querySelector('.full-stars'),
5.         rating       = parseFloat(starRating.dataset.rating) || 0,
6.         percentWidth = rating * 20;
7.     fullStars.style.width = percentWidth + '%';
8. }

Analizziamo il codice riga per riga.

Alla riga 1 rilevo nel DOM tutti gli elementi che hanno classe star-rating.

Alla riga 2 (che finisce alla riga 8), cicliamo tra tutti gli elementi. Avrei potuto utilizzare la funzione forEach direttamente sull’oggetto starRatings, ma non tutti i browser supportano tale funzione su una collection di elementi HTML, quindi, anziché usare qualche polyfill, preferisco fare un ciclo nella modalità classica.

Alla riga 3 estraggo un nodo dalla collezione di nodi e alla riga 4 identifico l’elemento con classe full-stars innestato nel nodo star-rating che sto elaborando.

Alla riga 5 prendo il valore dell’attributo custom data-rating e lo trasformo in un valore numerico. Come regola di fallback, se non dovesse esistere l’attributo data-rating oppure se il suo valore non dovesse essere un numero valido, la variabile rating avrà come risultato 0.

Alla riga 6 calcolo la percentuale: ho dato per assunto che data-rating sarà un numero compreso tra 0 e 5, dove 0 corrisponde a nessun riempimento e 5 corrisponde al riempimento al 100% delle stelle piene.

Alla riga 7 definisco la larghezza del box che contiene le stelle piene.

L’esempio è riportato di seguito:

Conclusione

Anche utilizzando JavaScript, mostrare un sistema di star rating ancora più accurato di quello raccontato nel precedente articolo, l’operazione non è stata così difficile e non ha richiesto la produzione di JavaScript, HTML o CSS di una certa complessità.

Inoltre l’esempio riportato funziona anche se ci sono più elementi con classe star-rating sulla stessa pagina… provare per credere! :-)

Se ti è piaciuto e vuoi leggere altri articoli come questo, condividilo.

Se ritieni che si possa risolvere in modo ancora più semplice questo problema, lascia un commento e condividiamo le tue riflessioni.