JavaScript를 사용하지 않고 별점 기능을 만들어보려고 합니다.
<body>
<div class="star-rating">
<input type="radio" class="star" value="1">
<input type="radio" class="star" value="2">
<input type="radio" class="star" value="3">
<input type="radio" class="star" value="4">
<input type="radio" class="star" value="5">
</div>
</body>
.star-rating {
display: flex;
}
.star {
appearance: none;
padding: 1px;
}
.star::after {
content: '☆';
color: hsl(60, 80%, 45%);
font-size: 20px;
}
별점 기능을 만들기 위해 별 모양을 표현하는 HTML과 CSS를 만들어줍니다.
평점은 1 ~ 5 사이의 값을 하나 가지기 때문에 input[type="radio"]
를 사용하였습니다.
appearance: none
은 기존의 라디오 모양을 표시하지 않도록 합니다. 대신에, ::after
를 사용하여 원하는 모양을 표현할 수 있습니다.
별 다섯개가 표시될 것입니다. 간단하게 하기 위해 ☆ 특수기호를 사용하였습니다.
:hover
.star-rating {
display: flex;
}
.star {
appearance: none;
padding: 1px;
}
.star::after {
content: '☆';
color: hsl(60, 80%, 45%);
font-size: 20px;
}
+.star:hover::after {
+ content: '★';
+}
마우스를 올릴 때 :hover
pseudo class가 활성화되면서 ★ 모양이 표시될 것입니다.
:has(~ :hover)
갑자기 어려운 쿼리가 나타났습니다. 하나씩 해석해보자면,
:has()
는 괄호 안의 선택자에 해당되는 엘리먼트를 가지고 있을 때 활성화될 것입니다. 기본적으로 자식 요소를 찾습니다.
하지만 괄호 안에서 물결표(~
)로 시작한다면, 자신의 뒤에 있는 요소를 찾습니다.
즉 .star:has(~ .star:hover)
는, 자신(.star
)의 뒤의 요소에 :hover
된 .star
가 있다면 활성화됩니다.
.star-rating {
display: flex;
}
.star {
appearance: none;
padding: 1px;
}
.star::after {
content: '☆';
color: hsl(60, 80%, 45%);
font-size: 20px;
}
.star:hover::after {
content: '★';
}
+.star:has(~ .star:hover)::after {
+ content: '★';
+}
클릭 시 별이 고정되도록 하기
라디오 버튼의 특성을 활용하여 클릭하였을 때 별이 고정되도록 할 수 있습니다.
.star-rating {
display: flex;
}
.star {
appearance: none;
padding: 1px;
}
.star::after {
content: '☆';
color: hsl(60, 80%, 45%);
font-size: 20px;
}
.star:hover::after {
content: '★';
}
.star:has(~ .star:hover)::after {
content: '★';
}
+.star:checked::after,
+.star:has(~ .star:checked)::after {
+ content: '★';
+}
라디오의 :checked
를 사용하면 체크되어 있는 라디오 버튼을 대상으로 할 수 있습니다.
체크되어 있는 자신과 이전 요소들이 ★ 모양으로 변경되도록 CSS를 추가해줍니다.
호버 중일 때 뒤의 별들은 모두 끄기
위의 실행결과 보고 어색하다고 느꼈을 겁니다. :hover
시 :hover
된 .star
를 기준으로 뒤의 .star
들이 모두 꺼져야 하기 때문입니다.
이 또한 CSS로 간단하게 처리할 수 있습니다.
.star-rating {
display: flex;
}
.star {
appearance: none;
padding: 1px;
}
.star::after {
content: '☆';
color: hsl(60, 80%, 45%);
font-size: 20px;
}
.star:hover::after {
content: '★';
}
.star:has(~ .star:hover)::after {
content: '★';
}
.star:checked::after,
.star:has(~ .star:checked)::after {
content: '★';
}
+.star:hover ~ .star::after {
+ content: '☆';
+}
물결표(A ~ B) 선택자는 A 뒤에 있는 B를 선택합니다. 즉 .star:hover ~ .star
는, :hover
되어있는 .star
뒤의 .star
가 선택됩니다.
CSS 축약하기
.star-rating {
display: flex;
}
.star {
appearance: none;
padding: 1px;
}
.star::after {
content: '☆';
color: hsl(60, 80%, 45%);
font-size: 20px;
}
-.star:hover::after {
- content: '★';
-}
-
-.star:has(~ .star:hover)::after {
- content: '★';
-}
-
-.star:checked::after,
-.star:has(~ .star:checked)::after {
- content: '★';
-}
+.star:hover::after,
+.star:has(~ .star:hover)::after,
+.star:checked::after,
+.star:has(~ .star:checked)::after {
+ content: '★';
+}
.star:hover ~ .star::after {
content: '☆';
}
동일한 스타일을 표현하는 선택자들은 콤마(,
)로 묶을 수 있습니다.
전체 소스코드
<!DOCTYPE html>
<html lang="ko">
<head>
<style>
.star-rating {
display: flex;
}
.star {
appearance: none;
padding: 1px;
}
.star::after {
content: '☆';
color: hsl(60, 80%, 45%);
font-size: 20px;
}
.star:hover::after,
.star:has(~ .star:hover)::after,
.star:checked::after,
.star:has(~ .star:checked)::after {
content: '★';
}
.star:hover ~ .star::after {
content: '☆';
}
</style>
</head>
<body>
<div class="star-rating">
<input type="radio" class="star" value="1">
<input type="radio" class="star" value="2">
<input type="radio" class="star" value="3">
<input type="radio" class="star" value="4">
<input type="radio" class="star" value="5">
</div>
</body>
</html>