Превратите число в отображение звездного рейтинга с помощью jQuery и CSS


101

Я смотрел плагин jquery и задавался вопросом, как адаптировать этот плагин, чтобы превратить число (например, 4.8618164) в 4.8618164 звезды, заполненные из 5. В основном интерпретация числа <5 в звезды, заполненные в 5-звездочной рейтинговой системе с использованием jQuery / JS / CSS.

Обратите внимание, что это будет отображать только рейтинг в звездах из уже доступного числа и не принимать новые представления оценок.


Ответы:


256

Вот решение для вас, использующее только одно очень маленькое и простое изображение и один автоматически сгенерированный элемент 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;
}

Образ

альтернативный текст
(источник: 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();
});

Вывод

Изображение из набора иконок фуги (www.pinvoke.com)
(источник: ulmanen.fi )

Демо

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

Вероятно, это подойдет вам. С помощью этого метода вам не нужно рассчитывать ширину звезды в три четверти или еще много чего, просто дайте ему поплавок, и он даст вам ваши звезды.


Небольшое объяснение того, как представлены звезды, было бы уместно.

Сценарий создает два элемента span на уровне блока. Оба интервала изначально получают размер 80 пикселей * 16 пикселей и фоновое изображение stars.png. Пролеты вложены друг в друга, поэтому структура пролетов выглядит так:

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

Внешний пролет получает background-positionоф 0 -16px. Это делает видимыми серые звезды во внешнем пролете. Поскольку внешний диапазон имеет высоту 16 пикселей и repeat-xбудет отображаться только 5 серых звезд.

Внутренняя оболочка с другой стороны , есть background-positionиз 0 0которых делает только желтые звезды видны.

Это, конечно, будет работать с двумя отдельными файлами изображений, star_yellow.png и star_gray.png. Но поскольку звезды имеют фиксированную высоту, мы можем легко объединить их в одно изображение. Это использует технику спрайтов CSS .

Теперь, когда пролеты вложены, они автоматически накладываются друг на друга. В случае по умолчанию, когда ширина обоих промежутков составляет 80 пикселей, желтые звезды полностью закрывают серые звезды.

Но когда мы регулируем ширину внутреннего пролета, ширина желтых звезд уменьшается, открывая серые звезды.

С точки зрения доступности было бы разумнее оставить число с плавающей запятой внутри внутреннего диапазона и скрыть его text-indent: -9999px, чтобы люди с выключенным CSS видели хотя бы число с плавающей запятой вместо звездочек.

Надеюсь, это имело смысл.


Обновлено 22.10.2010

Теперь еще компактнее и труднее для понимания! Также можно сжать до одного вкладыша:

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

@Tatu, есть ли шанс подробнее рассказать, как это работает. Мои познания в jQuery не совсем понятны. Я думаю, что понимаю, как CSS работает с повторением в направлении x и 16 * 5 = 80 бит и как вы регулируете ширину изображения желтой звезды, но как вы накладываете два изображения друг на друга из одно изображение, на котором одна звезда находится над другой? Поднимает ли -16px серую звезду на тот же уровень, что и желтая?
paxdiablo

Спасибо за обновление, @Tatu, теперь это имеет для меня больше смысла. К сожалению, я уже дал вам +1, поэтому я не могу сделать это снова, но, надеюсь, объяснение принесет вам еще (заслуженную) репутацию. Ура.
paxdiablo

2
@paxdiablo и другие, спасибо за поддержку. Возможно, мне стоит сделать это подходящим плагином для jQuery, поскольку этот метод, кажется, стал для многих неожиданностью :)
Тату Ульманен

@Tatu, большое спасибо. Все существующие пятизвездочные плагины, похоже, нуждаются в форме, которая подходит для ввода рейтинга, но слишком много для изменения представления числа.
vfilby

7
Или даже меньше: jsbin.com/IBIDalEn/2/edit (PS удалил ненужные вещи в JS, уменьшил селекторы CSS и использовал max-width).
Roko C. Buljan

30

Если вам нужно поддерживать только современные браузеры, вы можете обойтись:

  • Нет изображений;
  • В основном статический 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. Он позволяет набрать столько баллов, сколько у вас будет классов (потому что мы не можем использовать Javascript для точной widthустановки псевдоэлемента).

Чтобы исправить это, приведенное выше решение можно легко настроить. :beforeИ :afterбиты должны стать фактическими элементами в DOM (так что нам нужно некоторое JS для этого).

Последний оставлен в качестве упражнения для читателя.


3
Я расширил это решение, чтобы приспособить его к числам с плавающей запятой и динамически генерируемым баллам, которые не соответствуют 10-м от 100. Codepen . Спасибо за то, что начали: D
cameck

Это довольно милый сценарий
Ник Див

1
Идеальное решение. Вы сделали мой день . Спасибо, @Jeroen ... продолжай качать.
Прабху Нандан Кумар

22

Попробуйте эту вспомогательную функцию / файл 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> отсутствует :)
Роб Стоки,

7

Почему бы просто не иметь пять отдельных изображений звезды (пустое, заполненное на четверть, наполовину полное, заполненное на три четверти и полное), а затем просто вставить изображения в ваш DOM в зависимости от усеченного или округленного значения рейтинга, умноженного на 4 ( получить целую цифру по кварталам)?

Например, 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>

Чтобы облегчить доступ, я даже добавляю необработанное значение рейтинга в атрибут title.


4

используя jquery без прототипа, обновите код 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 class="stars" data-rating="4" ></span>

3

ДЕМО

Вы можете сделать это только с двумя изображениями. 1 пустая звезда, 1 закрашенная звезда.

Наложите заполненное изображение поверх другого. и преобразовать число рейтинга в проценты и использовать его как ширину изображения заливки.

введите описание изображения здесь

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

0

Вот мой вариант использования JSX и font 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>

Первая строка предназначена для всей звезды, а вторая строка - для половинной звезды (если есть).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.