Convierta un número en una pantalla de calificación de estrellas usando jQuery y CSS

101

He estado mirando el complemento jquery y me preguntaba cómo adaptar ese complemento para convertir un número (como 4.8618164) en un 4.8618164 estrellas llenas de 5. Básicamente interpretar un número <5 en estrellas llenado en un sistema de calificación de 5 estrellas usando jQuery / JS / CSS.

Tenga en cuenta que esto solo mostrará / mostraría la calificación de estrellas de un número ya disponible y no aceptará nuevas presentaciones de calificaciones.

Steve
fuente

Respuestas:

255

Aquí hay una solución para usted, usando solo una imagen muy pequeña y simple y un elemento span generado automáticamente:

CSS

span.stars, span.stars span {
    display: block;
    background: url(stars.png) 0 -16px repeat-x;
    width: 80px;
    height: 16px;
}

span.stars span {
    background-position: 0 0;
}

Imagen

texto alternativo
(fuente: ulmanen.fi )

Nota: ¡ NO enlace a la imagen de arriba! Copie el archivo en su propio servidor y utilícelo desde allí.

jQuery

$.fn.stars = function() {
    return $(this).each(function() {
        // Get the value
        var val = parseFloat($(this).html());
        // Make sure that the value is in 0 - 5 range, multiply to get width
        var size = Math.max(0, (Math.min(5, val))) * 16;
        // Create stars holder
        var $span = $('<span />').width(size);
        // Replace the numerical value with stars
        $(this).html($span);
    });
}

Si desea restringir las estrellas a solo la mitad o un cuarto de estrella, agregue una de estas filas antes de la var sizefila:

val = Math.round(val * 4) / 4; /* To round to nearest quarter */
val = Math.round(val * 2) / 2; /* To round to nearest half */

HTML

<span class="stars">4.8618164</span>
<span class="stars">2.6545344</span>
<span class="stars">0.5355</span>
<span class="stars">8</span>

Uso

$(function() {
    $('span.stars').stars();
});

Salida

Imagen del conjunto de iconos de fuga (www.pinvoke.com)
(fuente: ulmanen.fi )

Manifestación

http://www.ulmanen.fi/stuff/stars.php

Probablemente esto se adapte a sus necesidades. Con este método no tienes que calcular ningún ancho de estrella de tres cuartos o lo que sea, solo dale un flotador y te dará tus estrellas.


Una pequeña explicación sobre cómo se presentan las estrellas podría estar en orden.

El script crea dos elementos de tramo a nivel de bloque. Ambos tramos obtienen inicialmente un tamaño de 80px * 16px y una imagen de fondo stars.png. Los tramos están anidados, de modo que la estructura de los tramos se ve así:

<span class="stars">
    <span></span>
</span>

El tramo exterior obtiene una background-positionde 0 -16px. Eso hace visibles las estrellas grises en el tramo exterior. Como el tramo exterior tiene una altura de 16px y repeat-x, solo mostrará 5 estrellas grises.

El tramo interior por otro lado tiene una background-positionde0 0 cuales solo hace visibles las estrellas amarillas.

Por supuesto, esto funcionaría con dos archivos de imágenes separados, star_yellow.png y star_gray.png. Pero como las estrellas tienen una altura fija, podemos combinarlas fácilmente en una imagen. Esto utiliza la técnica de sprites CSS .

Ahora, a medida que se anidan los tramos, se superponen automáticamente entre sí. En el caso predeterminado, cuando el ancho de ambos tramos es 80px, las estrellas amarillas oscurecen por completo las estrellas grises.

Pero cuando ajustamos el ancho del tramo interior, el ancho de las estrellas amarillas disminuye, revelando las estrellas grises.

En cuanto a la accesibilidad, hubiera sido más prudente dejar el número flotante dentro del intervalo interno y ocultarlo text-indent: -9999px, para que las personas con CSS desactivado al menos vieran el número de punto flotante en lugar de las estrellas.

Ojalá tuviera algún sentido.


Actualizado 2010/10/22

¡Ahora aún más compacto y más difícil de entender! También se puede exprimir hasta formar un solo revestimiento:

$.fn.stars = function() {
    return $(this).each(function() {
        $(this).html($('<span />').width(Math.max(0, (Math.min(5, parseFloat($(this).html())))) * 16));
    });
}
Tatu Ulmanen
fuente
@Tatu, alguna posibilidad de desarrollarlo un poco más con cómo funciona esto. Mi conocimiento de jQuery es un poco corto para comprenderlo por completo. Creo que entiendo cómo funciona el CSS con la repetición en la dirección xy el 16 * 5 = 80 bit y cómo se ajusta el ancho de la imagen de la estrella amarilla, pero cómo se superponen las dos imágenes una encima de la otra desde un una sola imagen que tiene una estrella encima de la otra? ¿El -16px lleva la estrella gris al mismo nivel que la amarilla?
paxdiablo
Gracias por la actualización, @Tatu, eso tiene mucho más sentido para mí ahora. Desafortunadamente, ya te había dado un +1, así que no puedo volver a hacerlo, pero espero que la explicación te gane más reputación (bien merecida). Salud.
paxdiablo
2
@paxdiablo y otros, gracias por el apoyo. Quizás debería hacer de este un complemento de jQuery adecuado, ya que esta técnica parece sorprender a la mayoría :)
Tatu Ulmanen
@Tatu, muchas gracias. Todos los complementos de cinco estrellas existentes parecen querer un formulario que está bien para ingresar una calificación, pero es excesivo para cambiar la representación de un número.
vfilby
7
O incluso más pequeño: jsbin.com/IBIDalEn/2/edit (PS eliminó cosas innecesarias en JS, selectores de CSS minimizados y usados max-width).
Roko C. Buljan
30

Si solo tiene que admitir navegadores modernos, puede salirse con la suya:

  • Sin imágenes;
  • Parcialmente CSS estática;
  • Casi sin jQuery o Javascript;

Solo necesita convertir el número a class, por ejemplo class='stars-score-50'.

Primero una demostración de marcado "renderizado":

body { font-size: 18px; }

.stars-container {
  position: relative;
  display: inline-block;
  color: transparent;
}

.stars-container:before {
  position: absolute;
  top: 0;
  left: 0;
  content: '★★★★★';
  color: lightgray;
}

.stars-container:after {
  position: absolute;
  top: 0;
  left: 0;
  content: '★★★★★';
  color: gold;
  overflow: hidden;
}

.stars-0:after { width: 0%; }
.stars-10:after { width: 10%; }
.stars-20:after { width: 20%; }
.stars-30:after { width: 30%; }
.stars-40:after { width: 40%; }
.stars-50:after { width: 50%; }
.stars-60:after { width: 60%; }
.stars-70:after { width: 70%; }
.stars-80:after { width: 80%; }
.stars-90:after { width: 90%; }
.stars-100:after { width: 100; }
Within block level elements:

<div><span class="stars-container stars-0">★★★★★</span></div>
<div><span class="stars-container stars-10">★★★★★</span></div>
<div><span class="stars-container stars-20">★★★★★</span></div>
<div><span class="stars-container stars-30">★★★★★</span></div>
<div><span class="stars-container stars-40">★★★★★</span></div>
<div><span class="stars-container stars-50">★★★★★</span></div>
<div><span class="stars-container stars-60">★★★★★</span></div>
<div><span class="stars-container stars-70">★★★★★</span></div>
<div><span class="stars-container stars-80">★★★★★</span></div>
<div><span class="stars-container stars-90">★★★★★</span></div>
<div><span class="stars-container stars-100">★★★★★</span></div>

<p>Or use it in a sentence: <span class="stars-container stars-70">★★★★★</span> (cool, huh?).</p>

Luego, una demostración que usa un poquito de código:

$(function() {
  function addScore(score, $domElement) {
    $("<span class='stars-container'>")
      .addClass("stars-" + score.toString())
      .text("★★★★★")
      .appendTo($domElement);
  }

  addScore(70, $("#fixture"));
});
body { font-size: 18px; }

.stars-container {
  position: relative;
  display: inline-block;
  color: transparent;
}

.stars-container:before {
  position: absolute;
  top: 0;
  left: 0;
  content: '★★★★★';
  color: lightgray;
}

.stars-container:after {
  position: absolute;
  top: 0;
  left: 0;
  content: '★★★★★';
  color: gold;
  overflow: hidden;
}

.stars-0:after { width: 0%; }
.stars-10:after { width: 10%; }
.stars-20:after { width: 20%; }
.stars-30:after { width: 30%; }
.stars-40:after { width: 40%; }
.stars-50:after { width: 50%; }
.stars-60:after { width: 60%; }
.stars-70:after { width: 70%; }
.stars-80:after { width: 80%; }
.stars-90:after { width: 90%; }
.stars-100:after { width: 100; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Generated: <div id="fixture"></div>

Las mayores desventajas de esta solución son:

  1. Necesita las estrellas dentro del elemento para generar el ancho correcto;
  2. No hay marcado semántico, por ejemplo, prefiere la puntuación. como texto dentro del elemento;
  3. Solo permite tantas puntuaciones como clases tenga (porque no podemos usar Javascript para establecer una precisión widthen un pseudoelemento).

Para solucionar este problema, la solución anterior se puede modificar fácilmente. El :beforey:after bits deben convertirse en elementos reales en el DOM (por lo que necesitamos algo de JS para eso).

Este último se deja como ejercicio para el lector.

Jeroen
fuente
3
Extendí esta solución para que se adapte a los flotadores y las puntuaciones generadas dinámicamente que no encajan en décimas de 100. Codepen . Gracias por ayudarme a empezar: D
cameck
Este es un guión bastante dulce
Nick Div
1
Solución perfecta. Me has alegrado el día . Gracias @Jeroen ... sigue rockeando.
Prabhu Nandan Kumar
22

Pruebe esta función / archivo de ayuda de jquery

jquery.Rating.js

//ES5
$.fn.stars = function() {
    return $(this).each(function() {
        var rating = $(this).data("rating");
        var fullStar = new Array(Math.floor(rating + 1)).join('<i class="fas fa-star"></i>');
        var halfStar = ((rating%1) !== 0) ? '<i class="fas fa-star-half-alt"></i>': '';
        var noStar = new Array(Math.floor($(this).data("numStars") + 1 - rating)).join('<i class="far fa-star"></i>');
        $(this).html(fullStar + halfStar + noStar);
    });
}

//ES6
$.fn.stars = function() {
    return $(this).each(function() {
        const rating = $(this).data("rating");
        const numStars = $(this).data("numStars");
        const fullStar = '<i class="fas fa-star"></i>'.repeat(Math.floor(rating));
        const halfStar = (rating%1!== 0) ? '<i class="fas fa-star-half-alt"></i>': '';
        const noStar = '<i class="far fa-star"></i>'.repeat(Math.floor(numStars-rating));
        $(this).html(`${fullStar}${halfStar}${noStar}`);
    });
}

index.html

   <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Star Rating</title>
        <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" rel="stylesheet">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
        <script src="js/jquery.Rating.js"></script>
        <script>
            $(function(){
                $('.stars').stars();
            });
        </script>
    </head>
    <body>

        <span class="stars" data-rating="3.5" data-num-stars="5" ></span>

    </body>
    </html>

Captura de pantalla

Rajasekar D
fuente
Solo una nota de que falta la etiqueta de cierre </script> :)
Rob Stocki
7

¿Por qué no tener cinco imágenes separadas de una estrella (vacía, un cuarto lleno, medio lleno, tres cuartos lleno y lleno) y luego simplemente inyectar las imágenes en su DOM dependiendo del valor de calificación truncado o enrutado multiplicado por 4 ( para obtener un número entero para los trimestres)?

Por ejemplo, 4.8618164 multiplicado por 4 y redondeado es 19, que serían cuatro y tres cuartos de estrella.

Alternativamente (si eres perezoso como yo), solo selecciona una imagen de 21 (de 0 estrellas a 5 estrellas en incrementos de un cuarto) y selecciona la imagen única según el valor mencionado anteriormente. Entonces es solo un cálculo seguido de un cambio de imagen en el DOM (en lugar de intentar cambiar cinco imágenes diferentes).

paxdiablo
fuente
5

Terminé yendo totalmente libre de JS para evitar el retraso de procesamiento del lado del cliente. Para lograr eso, genero HTML como este:

<span class="stars" title="{value as decimal}">
    <span style="width={value/5*100}%;"/>
</span>

Para ayudar con la accesibilidad, incluso agrego el valor de calificación sin procesar en el atributo de título.

Tim Schmidt
fuente
4

usando jquery sin prototipo, actualice el código js a

$( ".stars" ).each(function() { 
    // Get the value
    var val = $(this).data("rating");
    // Make sure that the value is in 0 - 5 range, multiply to get width
    var size = Math.max(0, (Math.min(5, val))) * 16;
    // Create stars holder
    var $span = $('<span />').width(size);
    // Replace the numerical value with stars
    $(this).html($span);
});

También agregué un atributo de datos por el nombre de clasificación de datos en el intervalo.

<span class="stars" data-rating="4" ></span>
Galuga
fuente
2

MANIFESTACIÓN

Puedes hacerlo con solo 2 imágenes. 1 estrellas en blanco, 1 estrellas llenas.

Superponga la imagen llena en la parte superior de la otra. y convierta el número de clasificación en porcentaje y utilícelo como ancho de la imagen de relleno.

ingrese la descripción de la imagen aquí

.containerdiv {
  border: 0;
  float: left;
  position: relative;
  width: 300px;
} 
.cornerimage {
  border: 0;
  position: absolute;
  top: 0;
  left: 0;
  overflow: hidden;
 } 
 img{
   max-width: 300px;
 }
Elyor
fuente
0

Sin embargo, aquí está mi opinión sobre el uso de JSX y una fuente increíble, limitada en solo 0.5 precisión:

       <span>
          {Array(Math.floor(rating)).fill(<i className="fa fa-star"></i>)}
          {(rating) - Math.floor(rating)==0 ? ('') : (<i className="fa fa-star-half"></i>)}
       </span>

La primera fila es para toda la estrella y la segunda fila es para media estrella (si corresponde)

Ardhi
fuente