Transforme um número em exibição de classificação por estrelas usando jQuery e CSS

101

Estive olhando o plugin jquery e me perguntando como adaptá-lo para transformar um número (como 4,8618164) em 4,8618164 estrelas preenchidas com 5. Basicamente interpretando um número <5 em estrelas preenchidas em um sistema de classificação de 5 estrelas usando jQuery / JS / CSS.

Observe que isso só exibiria / mostraria a avaliação com estrelas de um número já disponível e não aceitaria novos envios de avaliações.

Steve
fonte

Respostas:

255

Aqui está uma solução para você, usando apenas uma imagem muito pequena e simples e um elemento span gerado automaticamente:

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;
}

Imagem

texto alternativo
(fonte: ulmanen.fi )

Nota: NÃO faça um hotlink para a imagem acima! Copie o arquivo para seu próprio servidor e use-o a partir daí.

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);
    });
}

Se você quiser restringir as estrelas a apenas metade ou um quarto do tamanho da estrela, adicione uma destas linhas antes da var sizelinha:

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();
});

Resultado

Imagem do conjunto de ícones de fuga (www.pinvoke.com)
(fonte: ulmanen.fi )

Demo

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

Isso provavelmente atenderá às suas necessidades. Com este método, você não precisa calcular três quartos ou qualquer outra largura de estrela, apenas dê um ponto flutuante e ele lhe dará suas estrelas.


Uma pequena explicação sobre como as estrelas são apresentadas pode ser adequada.

O script cria dois elementos de amplitude de nível de bloco. Ambos os spans inicialmente têm um tamanho de 80px * 16px e uma imagem de fundo stars.png. Os vãos são aninhados, de modo que a estrutura dos vãos tenha a seguinte aparência:

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

O vão externo recebe um valor background-positionde 0 -16px. Isso faz com que as estrelas cinzentas no vão externo sejam visíveis. Como o vão externo tem altura de 16 px erepeat-x , ele mostrará apenas 5 estrelas cinza.

O vão interno, por outro lado, tem um background-positionde0 0 que torna visíveis apenas as estrelas amarelas.

É claro que isso funcionaria com dois arquivos de imagem separados, star_yellow.png e star_gray.png. Mas como as estrelas têm uma altura fixa, podemos facilmente combiná-las em uma imagem. Isso utiliza a técnica de sprite CSS .

Agora, conforme os vãos são aninhados, eles são sobrepostos automaticamente. No caso padrão, quando a largura de ambos os vãos é de 80 px, as estrelas amarelas obscurecem completamente as estrelas cinzas.

Mas quando ajustamos a largura do vão interno, a largura das estrelas amarelas diminui, revelando as estrelas cinzas.

Em termos de acessibilidade, teria sido mais sensato deixar o número flutuante dentro do vão interno e ocultá-lo com text-indent: -9999px , para que as pessoas com CSS desativado pudessem pelo menos ver o número de ponto flutuante em vez das estrelas.

Esperançosamente, isso fez algum sentido.


Atualizado em 22/10/2010

Agora ainda mais compacto e difícil de entender! Também pode ser espremido em um forro:

$.fn.stars = function() {
    return $(this).each(function() {
        $(this).html($('<span />').width(Math.max(0, (Math.min(5, parseFloat($(this).html())))) * 16));
    });
}
Tatu Ulmanen
fonte
@Tatu, qualquer chance de dar mais detalhes sobre como isso funciona. Meu conhecimento de jQuery é um pouco curto para entendê-lo totalmente. Acho que entendo como o CSS funciona com a repetição na direção xe 16 * 5 = 80 bits e como você ajusta a largura da imagem da estrela amarela, mas como você está sobrepondo as duas imagens uma em cima da outra a partir de um única imagem que tem uma estrela acima da outra? O valor de -16 px eleva a estrela cinza ao mesmo nível da amarela?
paxdiablo
Obrigado pela atualização, @Tatu, que faz muito mais sentido para mim agora. Infelizmente, eu já tinha dado a você um +1, então não posso fazer isso de novo, mas espero que a explicação lhe garanta mais (bem merecida) reputação. Felicidades.
paxdiablo
2
@paxdiablo e outros, obrigado pelo apoio. Talvez eu deva fazer deste um plugin jQuery adequado, pois essa técnica parece ser uma surpresa para a maioria :)
Tatu Ulmanen
@Tatu, imensamente obrigado. Todos os plug-ins de cinco estrelas existentes parecem querer uma forma que seja adequada para inserir uma classificação, mas é um exagero alterar a representação de um número.
vfilby
7
Ou ainda menor: jsbin.com/IBIDalEn/2/edit (PS removeu coisas desnecessárias em JS, minimizou seletores de CSS e usou max-width).
Roko C. Buljan
30

Se você só precisa suportar navegadores modernos, pode se safar com:

  • Sem imagens;
  • Principalmente css estático;
  • Quase nenhum jQuery ou Javascript;

Você só precisa converter o número para um class, por exemplo class='stars-score-50'.

Primeiro, uma demonstração da marcação "renderizada":

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>

Em seguida, uma demonstração que usa um pequeno 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>

As maiores desvantagens desta solução são:

  1. Você precisa das estrelas dentro do elemento para gerar a largura correta;
  2. Não há marcação semântica, por exemplo, você prefere a pontuação como texto dentro do elemento;
  3. Ele só permite tantas pontuações quantas forem as aulas (porque não podemos usar Javascript para definir uma precisão widthem um pseudo-elemento).

Para corrigir isso, a solução acima pode ser facilmente ajustada. O :beforee:after bits precisam se tornar elementos reais no DOM (portanto, precisamos de algum JS para isso).

Este último é deixado como um exercício para o leitor.

Jeroen
fonte
3
Eu estendi esta solução para acomodar flutuações e pontuações geradas dinamicamente que não cabem em décimos de 100. Codepen . Obrigado por me começar: D
cameck
Este é um roteiro muito doce
Nick Div
1
Solução perfeita. Você fez meu dia . Obrigado @Jeroen ... continue arrasando.
Prabhu Nandan Kumar
22

Experimente esta função / arquivo auxiliar 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 tela

Rajasekar D
fonte
Apenas uma observação de que a tag de fechamento </script> está faltando :)
Rob Stocki
7

Por que não ter apenas cinco imagens separadas de uma estrela (vazio, um quarto cheio, meio cheio, três quartos cheio e cheio) e, em seguida, apenas injetar as imagens em seu DOM dependendo do valor truncado ou roteado de classificação multiplicado por 4 ( para obter um número inteiro para os quartos)?

Por exemplo, 4,8618164 multiplicado por 4 e arredondado é 19, que seriam quatro e três quartos de estrelas.

Alternativamente (se você for preguiçoso como eu), selecione apenas uma imagem de 21 (0 estrelas a 5 estrelas em incrementos de um quarto) e selecione a única imagem com base no valor mencionado. Então, é apenas um cálculo seguido por uma mudança de imagem no DOM (em vez de tentar mudar cinco imagens diferentes).

paxdiablo
fonte
5

Acabei ficando totalmente livre de JS para evitar o lag de renderização do lado do cliente. Para fazer isso, eu gero HTML assim:

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

Para ajudar na acessibilidade, adiciono até o valor bruto da classificação no atributo title.

Tim Schmidt
fonte
4

usando jquery sem protótipo, atualize o código js para

$( ".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);
});

Eu também adicionei um atributo de dados com o nome de classificação de dados no intervalo.

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

DEMO

Você pode fazer isso com apenas 2 imagens. 1 estrela em branco, 1 estrela cheia.

Sobrepor a imagem preenchida na parte superior da outra. e converter o número de classificação em porcentagem e usá-lo como largura da imagem de preenchimento.

insira a descrição da imagem aqui

.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
fonte
0

Aqui está minha opinião sobre o uso de JSX e fontes incríveis, limitada apenas na precisão de 0,5, embora:

       <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>

A primeira linha é para estrela inteira e a segunda linha é para meia estrela (se houver)

Ardhi
fonte