【CSS, JS】星評価レーティングの実装

# はじめに
こんにちは、どきどきのQiita初投稿です。
今回は巷でよく見る星マークで評価を視覚的に表示するアレをライブラリを使わず、実装してみました！
復習を兼ねて、振り返っていきたいと思います。

# なぜやろうと思ったか
[楽天トラベルキーワード検索API](https://webservice.rakuten.co.jp/documentation/keyword-hotel-search)を使うと平均レビューが小数点第二位までの数値で取得できるのですが、これを視覚的に星で表示させたいと思ったことがきっかけです。

# 土台となるHTML
ここに星を表示させていきます！
```html
<div class="card-review_star">
    <span class="card-review_number">4.27</span>
</div>
```

# どうやって星を表示するのか
cssの擬似要素を使って星を作っていきます。
まず::beforeで星の下地を作り、::afterで星の色を上から塗っていくイメージです。
```css
.card-review_star {
    position: relative;
    display: inline-block;
}

.card-review_star::before {
    content: "★★★★★";
    color: #CCCCCC; /* 星が塗られていない土台の色 */
}

.card-review_star::after {
    content: "★★★★★";
    position: absolute;
    width: 80%; /* ここの%によってどれぐらい星が塗られるのかが変わる */
    z-index: 1;
    top: 0;
    left: 0;
    overflow: hidden;
    white-space: nowrap;
    color: #ffcf32; /* 星自体の色 */
}

.card-review_number {
    display: none; /* 親要素のwidthから見て何%かを正しく反映させるために消す */
}
```
ここまでのコードで、星評価が表示されるようになります。
現在親要素に対してwidth:80%;で星を塗るように設定されているので、星が4つ塗られています。
![スクリーンショット 2024-10-31 13.43.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3889920/f9a61253-542c-01b4-340a-c525f4fdc6b8.png)

# 動的にjsで星の数を制御する
あとはAPIで取得した値を星に反映していきます。
```js
/**
 * 評価を星の数として出力します
 */
const createStar = () => {
  const starElement = document.querySelector('.card-review_star');
  const review = document.querySelector('.card-review_number').textContent;
  const roundReview = Math.round(review * 10) / 10;
  const widthPercentage = roundReview * 20;
  starElement.style.setProperty('--starWidth', `${widthPercentage}%`);
};

createStar();
```

## jsの説明
### ①星評価要素の取得
```js
const starElement = document.querySelector('.card-review_star');
const review = document.querySelector('.card-review_number').textContent;
```
星評価を動的に行うために、要素を取得します。

### ②レビューの四捨五入
```js
const roundReview = Math.round(review * 10) / 10;
```
今回、小数点第二位までAPIで取得できるので、小数点第一位までの値になるように四捨五入しています。(4.27 → 4.3)

### ③何%塗るかを計算するために、評価を20倍する
```js
const widthPercentage = roundReview * 20;
```
5でMAX評価、つまり100%になるので、100 / 5 = 20 となり、評価に対して20倍すれば良いことがわかります。
```
4.3 * 20 = 86
```
親要素に対して86%塗ればうまくいきそうです。

### ④setPropertyを使ってカスタムプロパティを設定
```js
starElement.style.setProperty('--starWidth', `${widthPercentage}%`);
```
今回`--starWidth`というカスタムプロパティを宣言して、レビュー評価の値に応じて`.card-review_star::after`のwidthが変化するように設定します。

# cssを変更
```css
.card-review_star::after {
  content: "★★★★★";
  position: absolute;
  width: var(--starWidth); /* 変更 */
  z-index: 1;
  top: 0;
  left: 0;
  overflow: hidden;
  white-space: nowrap;
  color: #ffcf32;
}
```
先ほど固定値にしていたwidthを変更します。

# 完成！
正しく星が表示されました！
(わかりやすくするために、画像では後ろにspanタグで数字を入れています)
![スクリーンショット 2024-10-31 15.10.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3889920/99f9b16d-3cdb-435c-9b78-0e688e9f41ad.png)

数値を変えても正しく動いていそうです^^
![スクリーンショット 2024-10-31 15.09.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3889920/14952579-369d-3784-ce11-436e8c593808.png)


# 参考にさせていただいたページ
ありがとうございました！

https://kinocolog.com/css_star_rating/

https://www.sejuku.net/blog/57084#index_id0

https://qiita.com/SeiuchiAzarashi/items/918daaa3aca55830ef32

https://developer.mozilla.org/ja/docs/Web/API/CSSStyleDeclaration/setProperty