Ubah angka menjadi tampilan peringkat bintang menggunakan jQuery dan CSS

101

Saya telah melihat plugin jquery dan bertanya-tanya bagaimana cara mengadaptasi plugin itu untuk mengubah angka (seperti 4.8618164) menjadi 4.8618164 bintang diisi dari 5. Pada dasarnya menafsirkan angka <5 menjadi bintang yang diisi dalam sistem peringkat bintang 5 menggunakan jQuery / JS / CSS.

Perhatikan bahwa ini hanya akan menampilkan / menampilkan peringkat bintang dari nomor yang sudah tersedia dan tidak menerima kiriman peringkat baru.

Steve
sumber

Jawaban:

255

Inilah solusi untuk Anda, dengan hanya menggunakan satu gambar yang sangat kecil dan sederhana dan satu elemen span yang dibuat secara otomatis:

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

Gambar

teks alt
(sumber: ulmanen.fi )

Catatan: jangan TIDAK hotlink ke gambar di atas! Salin file ke server Anda sendiri dan gunakan dari sana.

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

Jika Anda ingin membatasi bintang menjadi hanya setengah atau seperempat ukuran bintang, tambahkan salah satu baris berikut sebelum var sizebaris:

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>

Pemakaian

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

Keluaran

Gambar dari set ikon fugue (www.pinvoke.com)
(sumber: ulmanen.fi )

Demo

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

Ini mungkin sesuai dengan kebutuhan Anda. Dengan metode ini Anda tidak perlu menghitung lebar bintang tiga perempat atau yang lainnya, cukup berikan pelampung dan itu akan memberi Anda bintang Anda.


Penjelasan kecil tentang bagaimana bintang ditampilkan mungkin secara berurutan.

Skrip membuat dua elemen rentang level blok. Kedua bentang awalnya mendapatkan ukuran 80px * 16px dan gambar latar belakang stars.png. Span dibuat bertingkat, sehingga struktur bentang terlihat seperti ini:

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

Rentang luar mendapat nilai background-positionsebesar 0 -16px. Itu membuat bintang abu-abu di bentang luar terlihat. Karena rentang luar memiliki tinggi 16 piksel dan repeat-x, itu hanya akan menampilkan 5 bintang abu-abu.

Rentang batin di sisi lain memiliki background-positiondari 0 0yang membuat hanya bintang kuning terlihat.

Ini tentu saja akan bekerja dengan dua imagefile terpisah, star_yellow.png dan star_gray.png. Tetapi karena bintang-bintang memiliki ketinggian yang tetap, kita dapat dengan mudah menggabungkannya menjadi satu gambar. Ini menggunakan teknik sprite CSS .

Sekarang, saat bentang bersarang, bentang secara otomatis terhampar satu sama lain. Dalam kasus default, saat lebar kedua bentang adalah 80 piksel, bintang kuning sepenuhnya mengaburkan bintang abu-abu.

Tapi ketika kita menyesuaikan lebar bentang dalam, lebar bintang kuning berkurang, menampakkan bintang abu-abu.

Dari segi aksesibilitas, akan lebih bijaksana untuk meninggalkan nomor float di dalam span bagian dalam dan menyembunyikannya dengan text-indent: -9999px, sehingga orang dengan CSS dimatikan setidaknya akan melihat nomor floating point daripada bintang.

Semoga itu masuk akal.


Diperbarui 2010/10/22

Sekarang bahkan lebih kompak dan lebih sulit untuk dipahami! Bisa juga diperas menjadi satu lapisan:

$.fn.stars = function() {
    return $(this).each(function() {
        $(this).html($('<span />').width(Math.max(0, (Math.min(5, parseFloat($(this).html())))) * 16));
    });
}
Tatu Ulmanen
sumber
@Tatu, setiap kesempatan untuk menyempurnakannya dengan cara kerjanya. Pengetahuan saya tentang jQuery hanya sedikit untuk mendapatkannya. Saya rasa saya mengerti bagaimana CSS bekerja dengan pengulangan dalam arah x dan bit 16 * 5 = 80 dan bagaimana Anda menyesuaikan lebar gambar bintang kuning, tetapi bagaimana Anda melapisi dua gambar di atas satu sama lain dari a gambar tunggal yang memiliki satu bintang di atas yang lain? Apakah -16px membawa bintang abu-abu ke level yang sama dengan kuning?
paxdiablo
Terima kasih atas pembaruannya, @Tatu, itu jauh lebih masuk akal bagi saya sekarang. Sayangnya, saya sudah memberi Anda +1, jadi saya tidak bisa melakukannya lagi, tetapi semoga penjelasan ini memberi Anda reputasi (yang berpenghasilan baik) lagi. Bersulang.
paxdiablo
2
@paxdiablo dan lainnya terima kasih atas dukungannya. Mungkin saya harus menjadikan ini plugin jQuery yang tepat karena teknik ini tampaknya paling mengejutkan :)
Tatu Ulmanen
@Tatu, terima kasih banyak. Semua plugin bintang lima yang ada tampaknya menginginkan formulir yang bagus untuk memasukkan peringkat, tetapi terlalu berlebihan untuk mengubah representasi angka.
vfilby
7
Atau bahkan lebih kecil: jsbin.com/IBIDalEn/2/edit (PS menghapus hal-hal yang tidak dibutuhkan di JS, selektor CSS yang diperkecil dan digunakan max-width).
Roko C. Buljan
30

Jika Anda hanya harus mendukung peramban modern, Anda dapat melakukannya dengan:

  • Tidak ada gambar;
  • Kebanyakan css statis;
  • Hampir tidak ada jQuery atau Javascript;

Anda hanya perlu mengubah angkanya menjadi a class, mis class='stars-score-50'.

Pertama, demo markup "yang dirender":

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>

Kemudian demo yang menggunakan sedikit kode:

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

Kerugian terbesar dari solusi ini adalah:

  1. Anda membutuhkan bintang di dalam elemen untuk menghasilkan lebar yang benar;
  2. Tidak ada markup semantik, misalnya Anda lebih suka skor sebagai teks di dalam elemen;
  3. Ini hanya memungkinkan skor sebanyak yang Anda miliki di kelas (karena kita tidak dapat menggunakan Javascript untuk menyetel tepat widthpada elemen semu).

Untuk mengatasinya, solusi di atas dapat dengan mudah diubah. The :beforedan :afterbit perlu menjadi elemen yang sebenarnya di DOM (jadi kita perlu beberapa JS untuk itu).

Yang terakhir ini dibiarkan sebagai latihan bagi pembaca.

Jeroen
sumber
3
Saya memperluas solusi ini untuk mengakomodasi float dan skor yang dihasilkan secara dinamis yang tidak sesuai dengan 10 dari 100. Codepen . Terima kasih telah membantu saya memulai: D
cameck
Ini adalah skrip yang cukup manis
Nick Div
1
Solusi yang tepat. Anda membuat hari saya. Terima kasih @Jeroen ... terus goyang.
Prabhu Nandan Kumar
22

Coba fungsi / file pembantu jquery ini

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>

Screenshot

Rajasekar D
sumber
Perlu diketahui bahwa tag penutup </script> tidak ada :)
Rob Stocki
7

Mengapa tidak hanya memiliki lima gambar terpisah dari sebuah bintang (kosong, seperempat penuh, setengah penuh, tiga perempat penuh dan penuh) lalu masukkan saja gambar tersebut ke DOM Anda bergantung pada nilai peringkat yang terpotong atau kasar dikalikan dengan 4 ( untuk mendapatkan jumlah keseluruhan untuk perempat)?

Misalnya, 4,8618164 dikalikan dengan 4 dan dibulatkan adalah 19 yang akan menjadi bintang empat dan tiga perempat.

Alternatifnya (jika Anda malas seperti saya), cukup pilih satu gambar dari 21 (0 bintang hingga 5 bintang dengan kenaikan seperempat) dan pilih satu gambar berdasarkan nilai yang disebutkan di atas. Maka itu hanya satu kalkulasi yang diikuti dengan perubahan gambar di DOM (daripada mencoba mengubah lima gambar berbeda).

paxdiablo
sumber
5

Saya akhirnya benar-benar bebas JS untuk menghindari jeda render sisi klien. Untuk mencapai itu, saya menghasilkan HTML seperti ini:

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

Untuk membantu aksesibilitas, saya bahkan menambahkan nilai rating mentah di atribut title [judul].

Tim Schmidt
sumber
4

menggunakan jquery tanpa prototipe, perbarui kode js ke

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

Saya juga menambahkan atribut data dengan nama data-rating dalam rentang tersebut.

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

DEMO

Anda dapat melakukannya dengan 2 gambar saja. 1 bintang kosong, 1 bintang terisi.

Overlay gambar yang diisi di atas yang lain. dan ubah angka rating menjadi persentase dan gunakan sebagai lebar gambar pengisi.

masukkan deskripsi gambar di sini

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

Inilah pendapat saya menggunakan JSX dan font mengagumkan, terbatas hanya pada akurasi 0,5, meskipun:

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

Baris pertama untuk bintang utuh dan baris kedua untuk bintang setengah (jika ada)

Ardhi
sumber