Anonystick

anonystick@gmail.com

[Lab Javascript] - Tự code star rating chỉ với HTML, CSS, JS

Xin chào cộng đồng Anonystick! Đây là bài viết đầu tiên của mình ở anonystick.com, có gì sai sót mong nhận được góp ý từ các bạn.

Hôm nay vô tình có task trong trong dự án liên quan đến đánh giá xếp hạng. Mình tham khảo qua thì thấy star rating (đánh giá dùng biểu tượng ngôi sao) được dùng khá phổ biến. Trước đây chắc chắn quen thuộc nhất là hệ thống đánh giá nhà hàng và khách sạn với năm sao là chất lượng cao nhất.

Khalachackeo là có sẵn nhiều plugin, library hỗ trợ sẵn để các bạn làm việc này, nhưng hôm nay với tiêu chí "HẠN CHẾ TỐI DÙNG THƯ VIỆN NGOÀI" nên trong project và cũng trong tut này mình sẽ hướng dẫn các bạn làm star rating thuần bằng html, css.

Đầu tiên để làm 1 ngôi sao riêng lẽ mình sẽ dùng 2 thẻ:

+ input[type="radio"] để chứa value người dùng chọn. Lưu ý là các ngôi sao phải cùng name mục đích đảm bảo mỗi lần rating chỉ chọn duy nhất.

+ label để làm chỗ cho icon star và làm GUI cho input[type="radio"]

Lưu ý: chúng ta phải viết css theo thứ tự 5-1 thay vì 1-5, mình sẽ làm rõ hơn về chi tiết ở phần sau.

Các bạn có thể xem DEMO tại đây: SEE DEMO

I. HTML

I.a. HTML 5 level

<div id="rating">
    <input type="radio" id="star5" name="rating" value="5" />
    <label class = "full" for="star5" title="Awesome - 5 stars"></label>
 
    <input type="radio" id="star4" name="rating" value="4" />
    <label class = "full" for="star4" title="Pretty good - 4 stars"></label>
 
    <input type="radio" id="star3" name="rating" value="3" />
    <label class = "full" for="star3" title="Meh - 3 stars"></label>
 
    <input type="radio" id="star2" name="rating" value="2" />
    <label class = "full" for="star2" title="Kinda bad - 2 stars"></label>
 
    <input type="radio" id="star1" name="rating" value="1" />
    <label class = "full" for="star1" title="Sucks big time - 1 star"></label>
</div>

 Bên trên là phần html cho phép đánh giá đơn giản chỉ 5 mức: 1-5,  nếu các bạn muốn cho phép rating 10 mức: từ 0.5-5 thì chỉ việc thêm vào các group half xen kẽ với 5 group có sẵn là xong.

<div id="rating">
    <input type="radio" id="star5" name="rating" value="5" />
    <label class = "full" for="star5" title="Awesome - 5 stars"></label>
    
    <input type="radio" id="star4half" name="rating" value="4 and a half" />
    <label class="half" for="star4half" title="Pretty good - 4.5 stars"></label>
    
    <input type="radio" id="star4" name="rating" value="4" />
    <label class = "full" for="star4" title="Pretty good - 4 stars"></label>
    
    <input type="radio" id="star3half" name="rating" value="3 and a half" />
    <label class="half" for="star3half" title="Meh - 3.5 stars"></label>
    
    <input type="radio" id="star3" name="rating" value="3" />
    <label class = "full" for="star3" title="Meh - 3 stars"></label>
    
    <input type="radio" id="star2half" name="rating" value="2 and a half" />
    <label class="half" for="star2half" title="Kinda bad - 2.5 stars"></label>
    
    <input type="radio" id="star2" name="rating" value="2" />
    <label class = "full" for="star2" title="Kinda bad - 2 stars"></label>
    
    <input type="radio" id="star1half" name="rating" value="1 and a half" />
    <label class="half" for="star1half" title="Meh - 1.5 stars"></label>
    
    <input type="radio" id="star1" name="rating" value="1" />
    <label class = "full" for="star1" title="Sucks big time - 1 star"></label>
    
    <input type="radio" id="starhalf" name="rating" value="half" />
    <label class="half" for="starhalf" title="Sucks big time - 0.5 stars"></label>
</div>

Xong phần `html`, đến phần theo mình là quan trọng nhất trong tut, `CSS` để các bạn dễ dàng bám sát mình sẽ thêm trực tiếp comment vào những đoạn css quan trọng. Trước tiên cho dễ hình dung mình sẽ nói sơ về kỹ thuật css trong tut này, mình có dùng 2 selector css hơi lạ có thể các bạn không thường thấy:

+ Next siblings (dùng dấu +): secletor này sẽ giúp chúng ta lấy 1 thẻ tiếp theo ngay sau thẻ được chọn. Để hiểu rõ về next siblings các bạn có thể tham khảo thêm ở đây

+ All next siblings (dùng dấu ~): selector này có thể xem là mở rộng của next siblings, giúp chúng ta lấy tất cả thẻ cùng cấp được chỉ định tiếp theo thẻ được chọn. Thông tin thêm của All next siblings các bạn tham khảo ở đây

CSS hoàn toàn không có selector cho previous siblings, đây cũng là lý do để chúng ta lật ngược lại html như mình đã đề cập ở trên.

--

Lý thuyết là vậy, bây giờ chúng ta đến phần code css ngay sau đây.

II. CSS

/**
 * Để cho đơn giản nhất mình dùng icon star của font awesome.
 * À mà khoan, ở đây chúng ta chỉ làm tut nhỏ nên thấy import cả 1 library font-awesome có vẻ hơi quá đáng đã vậy còn ngược lại tiêu chí 
 * "HẠN CHẾ TỐI DÙNG THƯ VIỆN NGOÀI" :D chắc các bạn nghĩ thế nhỉ? Hiện tại chúng ta chỉ implement 1 tut nhỏ thì có vẻ thừa, 
 * nhưng trong project mình đang code có sẵn font-awesome nên trên thực tế mình đâu có * thêm gì đâu :D
 */
@import url(//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css);
/*reset css*/
div,label{margin:0;padding:0;}
body{margin:20px;}
h1{font-size:1.5em;margin:10px;}
/****** Style Star Rating Widget *****/
#rating{border:none;float:left;}
#rating>input{display:none;}/*ẩn input radio - vì chúng ta đã có label là GUI*/
#rating>label:before{margin:5px;font-size:1.25em;font-family:FontAwesome;display:inline-block;content:"\f005";}/*1 ngôi sao*/
#rating>.half:before{content:"\f089";position:absolute;}/*0.5 ngôi sao*/
#rating>label{color:#ddd;float:right;}/*float:right để lật ngược các ngôi sao lại đúng theo thứ tự trong thực tế*/
/*thêm màu cho sao đã chọn và các ngôi sao phía trước*/
#rating>input:checked~label,
#rating:not(:checked)>label:hover, 
#rating:not(:checked)>label:hover~label{color:#FFD700;}
/* Hover vào các sao phía trước ngôi sao đã chọn*/
#rating>input:checked+label:hover,
#rating>input:checked~label:hover,
#rating>label:hover~input:checked~label,
#rating>input:checked~label:hover~label{color:#FFED85;}

|||. JS

Các bạn có thể xem DEMO tại đây: SEE DEMO

Bên trên mình hướng dẫn bạn làm phần rating cho user chọn, để hiện kết quả thì bạn làm như sau:

+ Thêm attribute `disabled` co tất cả `input[type="radio"]`

+ Chạy hàm js sau của mình lúc load

function calcRate(r) {
 const f = ~~r,//Tương tự Math.floor(r)
 id = 'star' + f + (r % f ? 'half' : '')
 id && (document.getElementById(id).checked = !0)
}

Các bạn có thể xem DEMO tại đây: SEE DEMO
Cuối bài mình gửi lời cảm ơn các bạn đã kiên nhẫn đọc đến cuối, xin chào và hẹn gặp lại các bạn trong bài sau!

Chúc các bạn thành công!

Have a nice day!

Anph