프론트엔드
CSS로 별점 기능 만들기
solo5star
2023. 3. 27. 16:47
리뉴얼 된 블로그로 보기: https://solo5star.dev/posts/33/
JavaScript를 최대한 적게 사용하여 별점 기능을 만들어보려고 합니다.
body {
background: rgb(32, 32, 40);
}
.vote {
display: flex;
}
.star { padding: 1px; }
.star::after {
content: '☆';
color: hsl(60, 80%, 50%);
font-size: 20px;
}
<body>
<div class="vote">
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
</div>
</body>
별 모양을 표현하는 CSS를 만들어줍니다. 그리고 위의 HTML과 같이 마크업을 합니다.
별 다섯개가 표시될 것입니다. 간단하게 하기 위해 ☆ 특수기호를 사용하였습니다.
:hover
.star:hover::after {
content: '★';
}
마우스를 올릴 때 :hover pseudo class가 활성화될 것입니다.
:has(~ :hover)
갑자기 어려운 쿼리가 나타났습니다. 하나씩 해석해보자면,
:has() 는 괄호 안의 선택자에 해당되는 엘리먼트를 가지고 있을 때 활성화될 것입니다. 기본적으로 자식 요소를 찾습니다.
하지만 괄호 안에서 물결표(~)로 시작한다면, 자신의 뒤에 있는 요소를 찾습니다.
즉 .star:has(~ .star:hover) 는, 자신(.star)의 뒤의 요소에 :hover된 .star가 있다면 활성화됩니다.
.star:has(~ .star:hover)::after {
content: '★';
}
클릭 시 별이 고정되도록 하기
아쉽게도 이 로직은 CSS로 불가능하며, JavaScript가 필요합니다.
document.querySelectorAll('.star').forEach(($star) => {
$star.addEventListener('click', ({ target }) => {
// 모든 .star에서 .active 삭제
document.querySelectorAll('.star').forEach(($star) => {
$star.classList.remove('active');
});
// 클릭한 .star에 .active 추가
target.classList.add('active');
});
});
클릭하였을 때 클릭된 .star에 .active를 추가하는 코드입니다.
.star.active::after,
.star:has(~ .star.active)::after {
content: '★';
}
그리고 :hover에서 했던 것처럼 비슷하게 CSS를 추가하면 됩니다.
호버 중일 때 뒤의 별들은 모두 끄기
위의 Gif를 보고 어색하다고 느꼈을 겁니다. :hover 시 :hover된 .star를 기준으로 뒤의 .star들이 모두 꺼져야 하기 때문입니다.
이 또한 CSS로 간단하게 처리할 수 있습니다.
.star:hover ~ .star::after {
content: '☆';
}
물결표(A ~ B) 선택자는 A 뒤에 있는 B를 선택합니다. 즉 .star:hover ~ .star 는, :hover되어있는 .star 뒤의 .star가 선택됩니다.
전체 소스코드
<!DOCTYPE html>
<html lang="ko">
<head>
<style>
body {
background: rgb(32, 32, 40);
}
.vote {
display: flex;
}
.star { padding: 1px; }
.star::after {
content: '☆';
color: hsl(60, 80%, 50%);
font-size: 20px;
}
.star:hover::after,
.star:has(~ .star:hover)::after,
.star.active::after,
.star:has(~ .star.active)::after {
content: '★';
}
.star:hover ~ .star::after {
content: '☆';
}
</style>
</head>
<body>
<div class="vote">
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
<div class="star"></div>
</div>
</body>
<script>
document.querySelectorAll('.star').forEach(($star) => {
$star.addEventListener('click', ({ target }) => {
// 모든 .star에서 .active 삭제
document.querySelectorAll('.star').forEach(($star) => {
$star.classList.remove('active');
});
// 클릭한 .star에 .active 추가
target.classList.add('active');
});
});
</script>
</html>