使用jQuery和CSS将数字转换为星级评价显示

106

我一直在研究jQuery插件,想知道如何将该插件适应于将数字(例如4.8618164)转换为5颗星中的4.8618164颗填充的星星。基本上使用jQuery/JS/CSS将小于5的数字解释为在5星评级系统中填充的星星。

请注意,这只会显示已有数字的星级评分,而不接受新的评分提交。


这是一个美妙且易于使用的插件。链接 - wpcoder
这个纯CSS编写的代码无需JavaScript来显示星形图标(但要动态设置值可能需要JavaScript)。https://codepen.io/jamesbarnett/pen/vlpkh - User
9个回答

258

以下是一种解决方案,仅使用一个非常小且简单的图像和一个自动生成的span元素:

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

图片

alt文本
(来源: ulmanen.fi)

注意: 不要热链接到上面的图片!将文件复制到您自己的服务器上,然后从那里使用它。

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

如果你想将评分限制为只有半颗或四分之一颗星,那么请在var size这一行之前添加以下其中一行:

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>

使用方法

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

输出

Image from fugue icon set (www.pinvoke.com)
(来源:ulmanen.fi)

演示

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

这个方法可能适合您的需求。使用此方法,您不必计算任何三分之四或类似的星宽度,只需提供一个浮点数,它就会为您提供星级。


这里简要解释一下星级的呈现方式。

该脚本创建了两个块级 span 元素。两个 span 初始大小均为 80px * 16px,背景图片为 stars.png。这些 span 嵌套,使得结构看起来像这样:

<span class="stars">
    <span></span>
</span>
外部的元素设置了background-position0 -16px,这样外部元素中的灰色星星就会显示出来。由于外部元素高度为16像素,并采用repeat-x方式,因此只会显示5个灰色星星。

另一方面,内部的元素设置了background-position0 0,这样只有黄色星星才能显示。

当然,如果使用两个单独的图像文件star_yellow.png和star_gray.png,这也可以实现。但是由于星星高度固定,我们可以将它们轻松结合成一个图像。这利用了CSS sprite技术

现在,由于这两个元素嵌套在一起,它们会自动重叠在一起。默认情况下,当两个元素的宽度都为80像素时,黄色星星完全遮挡了灰色星星。

但是当我们调整内部元素的宽度时,黄色星星的宽度会减小,揭示出灰色星星。

从可访问性的角度来看,最好将浮点数留在内部元素中,并使用text-indent: -9999px将其隐藏起来,这样即使关闭了 CSS 的人也至少能看到浮点数而不是星星。

希望这有一定的意义。

更新于2010年10月22日

现在更加紧凑且更难理解!还可以缩成一行:

$.fn.stars = function() {
    return $(this).each(function() {
        $(this).html($('<span />').width(Math.max(0, (Math.min(5, parseFloat($(this).html())))) * 16));
    });
}

2
@paxdiablo和其他人,感谢你们的支持。也许我应该将这个技巧制作成一个正式的jQuery插件,因为这种技术似乎对大多数人来说都是一个惊喜 :) - Tatu Ulmanen
@Tatu Ulmanen:绝对值得一加和更多!我正在寻找这样的东西,但我还需要能够接受新的评分提交。你已经把它变成一个完整的插件了吗?如果没有,你能否给出如何接受评分提交的提示?非常感谢! :) - Kassem
对于热链接我深表歉意,当时没有好好思考。我已经修改了我的 JSBin 使用此链接:https://dl.dropboxusercontent.com/u/2176587/Assets/star.png - coderberry
您可以在内部span上指定百分比宽度,例如.css('width', Math.round(4.1 / 5 * 100) + '%') /* 82% */。在我看来这样更容易。 - Salman A
8
甚至更小:http://jsbin.com/IBIDalEn/2/edit(注:删除JS中不必要的内容,缩小CSS选择器并使用`max-width`)。 - Roko C. Buljan
显示剩余10条评论

40

到了2022年,现代浏览器提供了更多新的方法,如果您能在正确的位置生成评分/百分比,就需要更少的运行时代码。

让您的框架在正确的样式位置生成此HTML,例如33.333%,然后您就可以开始了:

.star-rating::before {
  /* What also works: "⭐⭐⭐⭐⭐" or "" or other characters. */
  content: "⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐";
}

.star-rating {
    display: inline-block;
    background-clip: text;
    -webkit-background-clip: text;
    color: rgba(0, 0, 0, 0.1);
}

body {
  font-size: 32px; /* for demo purpose */
}
<div
  class="star-rating"
  style="background-image: linear-gradient(
      to right,
      gold 0%,
      gold 33.333%,
      transparent 33.333%,
      transparent 100%);
  "
><!-- Don't forget to add an accessible alternative! --></div>


如果你只需要支持现代浏览器,那么你可以使用以下方法:

  • 图像;
  • 大部分静态 CSS 代码;
  • 几乎没有 jQuery 或 Javascript 代码;

你只需要将数字转换为一个 class,例如 class='stars-score-50'

首先演示一下“渲染”后的标记:

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>

然后是一个使用少量代码的演示:

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

这种解决方案的最大缺点是:
  1. 您需要在元素内部放置星星以生成正确的宽度;
  2. 没有语义化标记,例如您希望将 分数 作为元素内的文本;
  3. 它只允许与您拥有的班级数量相同的分数(因为我们无法在伪元素上设置精确的 width,所以不能使用 Javascript)。
要解决这个问题,可以轻松地调整上述解决方案。需要将 :before:after 部分变成 DOM 中的实际元素(因此我们需要一些 JS)。
后者留给读者作为练习。

5
我将这个解决方案扩展到可以处理浮点数和动态生成的分数,它们不能按100的十分之一来计算。Codepen。感谢你的帮助让我起步:D - cameck
这是一个相当不错的脚本。 - Nick Div
1
完美的解决方案。你让我的一天变得美好。谢谢@Jeroen...继续加油。 - Prabhu Nandan Kumar
1
绝妙的解决方案。 - Keyur Chavda-kc1994

26

试试这个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>

屏幕截图


只是提醒一下,缺少闭合的</script>标签 :) - Rob Stocki

7
为什么不只使用五个单独的星形图像(空的,四分之一满,半满,四分之三满和满)然后根据评分乘以4的截断或舍入值将图像注入到您的DOM中(以获取四分之一的整数)?
例如,4.8618164乘以4并舍入为19,这将是四个和三分之一颗星。
或者(如果您像我一样懒),只选择一个包含21个图像的图像(0颗星到5颗星,每次增加四分之一),并根据上述值选择单个图像。 然后只需进行一次计算,然后在DOM中更改图像(而不是尝试更改五个不同的图像)。

5

为避免客户端渲染延迟,我最终完全不使用JS。为了实现这一点,我生成如下的HTML:

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

为了提高可访问性,我甚至在标题属性中添加了原始评分值。

4

演示

您只需使用两张图片,一张是空星,一张是实心星。

将实心图片覆盖在另一张上面,并将评分数字转换为百分比,用作填充图像的宽度。

输入图像描述

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

3

使用jQuery而不是Prototype,将JS代码更新为:

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

我还在span标签中添加了一个名为data-rating的数据属性。
<span class="stars" data-rating="4" ></span>

3

CSS 中的星级评分

  • 使用 background-clip 以线性渐变背景绘制星星符号
  • 使用 CSS var() 将评分值从模板传递给渐变图像百分比

[style^=--rating]::after {
  content: "★★★★★";
  font-size: 2em;
  white-space: nowrap;
  background: linear-gradient(90deg, #fb0 calc(var(--rating) * 20%), #ddd calc(var(--rating) * 20%));
  background-clip: text;
  -webkit-background-clip: text;
  color: #0000;
}
<span style="--rating:5.0"></span> <br>
<span style="--rating:1.7"></span> <br>
<span style="--rating:3.5"></span> <br>

为了提高可访问性,您还可以添加Aria属性,例如aria-label="评分:4.5颗星"

使用CSS和JavaScript进行星级评分

虽然上面的示例非常适合用于评分预览目的,但如果您想让用户投票,您需要添加一些处理点击事件、指针交互时的UX颜色变化等JavaScript代码。请参考以下相关答案:
带有指针事件的星级评分


1

以下是我使用JSX和字体awesome的看法,精度仅限于0.5:

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

第一行是整颗星星,第二行是半颗星星(如果有的话)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接