2022. 11. 26. 17:15ㆍWEB Dev/Javascript | REACT | Node.js
회사에서 원래 rateit.js 라이브러리를 사용해서 리뷰 기능을 구축했는데, 제이쿼리 충돌로 인해 (하.. 카페24) 바닐라로 별점 기능을 급하게 구성하게 되었다.
input type 중 range를 사용했는데, range 사용도 처음이거니와 이 것과 CSS를 이용해서 0.5개 별점을 만들 수 있다는 점에서 다양한 곳에서 다양한 디자인으로 사용할 수 있을 것 같아서 적어본다.
알고보니 rateit.js 라이브러리도 동일한 방법으로 리뷰 기능을 만드는 것이었다.
보통 별점 기능은 0.5개 단위로 5점까지 표시되게 된다. 0점 부터 시작하는 곳도 있고, 0.5점부터 시작하는 곳이 있는데
우리는 0점부터 시작하되, 0점이면 리뷰 입력이 안되는 UX를 가지고 있다.
HTML, CSS, JAVASCRIPT 로 별점 기능 만들기
1. HTML로 뼈대 생성
원하는 곳에 HTML을 깔아준다. 보통 별점이기 때문에 별 모양의 특수문자를 넣어주었고 span 안에 span과 input이 있다.
가장 바깥을 감싸는 span에 있는 별은 아무것도 선택하지 않은 회색의 비활성화 별점을 표현하고
자식으로 들어가 있는 span이 색상을 표현하게 된다.
그리고 input[type="range"]는 드래그 했을 때 별점을 입력할 수 있도록 하는 역할을 한다.
<span class="rate">
★★★★★
<span class="active_rate">★★★★★</span>
<input type="range" class="rate_range" value="0" step="1" min="0" max="10">
</span>
CSS가 없는 상태에서는 이렇게 단순하게 표현된다.
2. CSS로 모양 만들기
CSS로 .active_rate가 .rate 별모양의 위로 가고, 그 위에 input[type="range"]가 위로 가도록 해야 한다.
가장 바깥의 span을 position: relative로 주고 나머지를 absolute로 처리한다.
그리고 왼쪽 상단 꼭지점으로 정렬하기 위해서 input[type="range"]와 .active_rate 모두에게 top: 0, left: 0을 준다.
별 모양은 특수문자로 font-size로 조절할 수 있기 때문에 span 전체에 font-size를 적절하게 준다.
여기까지 하면 아래와 같은 모양이 나온다.
빨간색 테두리가 있는 부분이 가장 밑에 깔리게 된 .rate의 별이고 그 위에 .active_rate, 그 위에 input[type="range"]가 올라오게 된다.
input[type="range"]가 약간 위로 올라가 있기 때문에 가운데 값을 맞춰주기 위해서 width와 height를 100%로 준다.
그럼 input[type="range"]의 범위가 별 전체를 덮을 수 있게 된다.
이제 .rate의 border값을 없애고 (빨간 박스 없애고) input[type="range"]의 투명도를 준다.
보통 어떤 요소를 감추거나 할 때 visibility: hidden;이나 display: none; 등을 주는데 둘 다 진짜로 안보이게 하고 기능을 사용할 수 없게 만드는 것이기 때문에 (display: none; 은 스크린 리더에서도 없애버린다)
opacity를 이용할 것이다. opacity는 불투명도이기 때문에 불투명한 값을 0으로 만들면 input[type="range"]가 보이지 않지만 input의 기능은 사용할 수 있다.
미리 넣어버리면 만들기 어렵기 때문에 가장 마지막에 넣을 것이다.
span{
font-size: 30px; /*span 공통 사이즈*/
}
.rate{
position: relative; /*기준*/
color: gray; /*별의 바탕색*/
}
.active_rate{
position: absolute; /*.rate 위로*/
color: yellow; /*활성화된 별*/
filter: drop-shadow(0 0 0.1rem #aaa); /*그림자 효과*/
top: 0; /*왼쪽 상단으로 정렬*/
left: 0; /*왼쪽 상단으로 정렬*/
}
.rate_range{
position: absolute; /*.active_rate 위로*/ top: 0; /*왼쪽 상단으로 정렬*/
left: 0; /*왼쪽 상단으로 정렬*/
widht: 100%; /*별의 전체 너비에 꽉 차게*/
height: 100%; /*별의 전체 높이에 꽉 차게*/
opacity: 0; /*작업 다하고 마지막에 넣기*/
}
3. CSS으로 별점 가리기
이 상태로는 별점을 사용할 수 없기 때문에 이제 기능을 만든다.
원리는 input[type="range"]가 드래그 되면 위에 덧씌워진 .active_rate(노란색 별점)이 0.5개부터 5개까지 보이도록 해야 한다.
이 기능은 .active_rate의 width를 조절하면 만들 수 있다.
기능의 이해를 위해 .active_rate에게 width 80%를 주어보자.
<span class="rate">
★★★★★
<span class="active_rate" style="width: 80%">★★★★★</span>
<input type="range" class="rate_range" value="0" step="1" min="0" max="10">
</span>
별다른 변화가 없을 것이다.
이유는 .active_rate는 현재 width: 80%만 가지고 있지만 나머니 20%가 다 보여질 수 있는 상태이기 때문이다.
바로 oveflow인데, 요소의 width값보다 내용물이 넘친다면 어떻게 처리할 지 CSS로 다룰 수 있는 속성으로 기본값이 overflow: visible;다. 우리는 .active_rate에게 overflow 속성을 아무것도 지정하지 않았기 때문에 overflow: visible; 상태로 나머지 20%가 그냥 보여지도록(visible) 해두었기 때문이다.
그래서 .active_rate에게 overflow: hidden;을 주게 되면 내가 지정한 width 만큼만 별(내부 콘텐츠)를 보여주게 된다.
.active_rate{
position: absolute; /*.rate 위로*/
color: yellow; /*활성화된 별*/
filter: drop-shadow(0 0 0.1rem #aaa); /*그림자 효과*/
top: 0; /*왼쪽 상단으로 정렬*/
left: 0; /*왼쪽 상단으로 정렬*/
overflow: hidden; /*width넘치는 것은 가림*/
}
1개의 별이 깔끔하게 가려졌다!
여기서 참고할 것은 80%일 때 별이 1개 가려진다는 것.
우리는 위에서 HTML을 만들 때 input[type="range"]에게 value를 0으로 step을 1로, min은 0, max는 10으로 주었는데, 이렇게 하면 내가 range의 파란색 동그라미(핸들)을 아무리 섬세하게 끌려고 해도 가로 길이의 10%씩만 움직이게 된다.
그리고 값은 0부터 max인 10까지, 0에서 시작해서 1개씩 늘어나게 된다.
이제 이 방법을 가지고 자바스크립트를 이용해 노란색 별인 .active_rate의 width 길이를 조절해보자.
4. 자바스크립트로 기능 만들기
input[type="range"]는 0에서부터 10까지의 값을 가지고 있다. 이 값을 받아오기 위해서는 input[type="range"]의 value를 받아와야 하는데, input[type="range"]에게 이벤트리스너를 붙여보자.
input[type="range"]의 클래스명은 rate_range로 .rate_range를 querySelector로 지정한다.
const inputRange = document.querySelector(".rate_range");
이 inputRange에게 이벤트리스너를 붙인다. 그리고 input[type="range"]의 경우 click이나 drag도 많이 사용하지만 딱 멈췄을 때의 값을 받아오기 위해서 input이라는 이벤트를 사용한다.
const inputRange = document.querySelector(".rate_range");
inputRange.addEventListener("input", ()=>{
})
그리고 inputRange의 값을 받아오기 위해서 event 인자로 값을 받고 event.target으로 현재 .rate_range를 받아온다.
const inputRange = document.querySelector(".rate_range");
inputRange.addEventListener("input",(event)=>{
const target = event.target;
const range = target.value;
})
이제 range 변수를 콘솔에 찍어보면 의도한대로 0부터 10까지의 값이 나오는 것을 볼 수 있다.
아까 .active_rate에 width를 주었던 것을 기억해보면 별이 아예 없을 때는 0%, 꽉찼을 때는 100%의 width를 주면 된다는 것을 추측해볼 수 있다.
결곡 .active_rate에게 width를 얼만큼 주느냐에 따라 별의 채워짐이 결정이 될 건데, 그럼 input 이벤트가 발생할 때마다 .active_rate의 width 값을 자바스크립트로 변경해주면 된다.
const activeRate = document.querySelector(".active_rate");
위와 같이 .active_range를 변수에 할당해주고 activeRate.style.width="값"; 을 할당해주어야 하기 때문에 아래와 같이 변경한다.
const inputRange = document.querySelector(".rate_range");
const activeRate = document.querySelector(".active_rate");
inputRange.addEventListener("input",(event)=>{
const target = event.target;
const range = target.value;
activeRate.style.width = `${range * 10}%`;
})
그럼 이렇게 보이게 된다.
잘 보면 별 반 개도 정확하게 표시된다!
하지만 처음 시작할 때 별이 꽉 찬 상태에서 시작하기 때문에 .active_rate의 최초 width 값을 0으로 만들어 주어야 한다.
그럼 이제 회색 별에서 시작해서 노란색 별이 채워지는 것처럼 보이게 된다.
그럼 이제 input[type="range"]는 감춰도 되니까 opacity를 0으로 만들어 준다.
span{
font-size: 30px; /*span 공통 사이즈*/
}
.rate{
position: relative; /*기준*/
color: gray; /*별의 바탕색*/
}
.active_rate{
position: absolute; /*.rate 위로*/
color: yellow; /*활성화된 별*/
filter: drop-shadow(0 0 0.1rem #aaa); /*그림자 효과*/
top: 0; /*왼쪽 상단으로 정렬*/
left: 0; /*왼쪽 상단으로 정렬*/
width: 0;
overflow: hidden; /*width넘치는 것은 가림*/
}
.rate_range{
position: absolute; /*.active_rate 위로*/
top: 0; /*왼쪽 상단으로 정렬*/
left: 0; /*왼쪽 상단으로 정렬*/
widht: 100%; /*별의 전체 너비에 꽉 차게*/
height: 100%; /*별의 전체 높이에 꽉 차게*/
opacity: 0; /*작업 다하고 마지막에 넣기*/
}
하지만... 별점은 0부터 5까지인데 input[type="range"]의 value는 0부터 10까지다.
그리고 어디에도 저장되지 않고 있는데...
5. 별점 값 저장하기
0부터 5 그리고 0부터 10.
결국엔 별점은 value를 2로 나눈 것이다.
우리가 아까 받아온 range라는 변수를 / 2하면 별점 값이 나온다. 아래와 같이 변수로 받아보자.
우선 HTML에도 표시하기 위해서 아래와 같이 p 요소를 하나 생성해주고
<span class="rate">
★★★★★
<span class="active_rate">★★★★★</span>
<input type="range" class="rate_range" value="0" step="1" min="0" max="10">
</span>
<p class="range_text">0점<p>
자바스크립트 함수도 이렇게 변경한다.
const inputRange = document.querySelector(".rate_range");
const activeRate = document.querySelector(".active_rate");
inputRange.addEventListener("input",(event)=>{
const target = event.target;
const range = target.value;
const rate = range / 2;
activeRate.style.width = `${range * 10}%`;
document.querySelector(".range_text").innerHTML = rate + "점";
})
이렇게 변경하면 아래와 같이 별점을 볼 수 있다.
굳이 어딘가에 저장할 일이 없다면 이대로 끝내면 되지만 보통 별점은 기록의 의미로 대부분 저장을 하는 데이터가 된다.
그래서 데이터를 프론트에서 임시로 저장하기 위해 input[type="hidden"]을 하나 만들어준다.
<input type="hidden" name="rate_value" id="rate_value" value="">
위에서 input 이벤트로 p 태그에 표시하는 것처럼 바로바로 #rate_value에 저장시켜보자
const inputRange = document.querySelector(".rate_range");
const activeRate = document.querySelector(".active_rate");
const rateValue = document.querySelector("#rate_value");
inputRange.addEventListener("input",(event)=>{
const target = event.target;
const range = target.value;
const rate = range / 2;
activeRate.style.width = `${range * 10}%`;
rateValue.value = rate;
console.log("#rate_value value : ", rateValue.value);
document.querySelector(".range_text").innerHTML = rate + "점";
})
이제 콘솔에 표시되는 input#rate_value의 값을 보자.
잘 저장된다!
별점 기능은 완성이고 추후 ajax나 fetch로 값을 POST할 때 input#rate_value의 value값만 보내주면 된다.
See the Pen Vanilla Rate by kenna-hwa (@kenna-hwa) on CodePen.
'WEB Dev > Javascript | REACT | Node.js' 카테고리의 다른 글
[React] 리액트 포탈 React Portal을 통해 모달 만들기 (0) | 2023.03.05 |
---|---|
[React] npm 대신 yarn으로 create-react-app 시작하기 (0) | 2023.02.26 |
[Javascript] fetch에서 HTTP 헤더 사용하기 (0) | 2022.11.12 |
[Javascript] DOM의 변화를 감시하는 MutationObserver 사용하기 (0) | 2022.10.28 |
[React] Recoil 내 Selector 간략 정리 (0) | 2022.07.31 |