1. 분석
Jquery나 다른 API를 사용하지 않고 순수 자바스크립트만을 이용해 슬라이드를 네비게이션 버튼을 구현하려 한다.
자바스크립트의 DOM 조작과 CSS의 flex, position을 이용해 구현해본다.
슬라이드 컨테이너, 슬라이드 아이템의 개념을 이해한다.
이전, 이후 버튼 클릭 시 슬라이드 아이템에 left offset을 적용시켜 이동할 수 있도록 구현할 것이다.
이동하는 과정에서 현재 슬라이드 위치를 기억하는 변수를 만들어 첫번째와 마지막 슬라이드 이외로 넘어가지 않도록 조건문을 걸어 슬라이드가 이상하게 작동하지 않도록 예방한다.
2. 구현 결과 미리보기
구현된 버튼 슬라이드는 아래와 같이 작동한다.
3. 구현
3.1. HTML - index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vanilla js slide with button</title>
<link rel="stylesheet" href="./slide.css" />
</head>
<body>
<div class="slide">
<div class="slide_item">1</div>
<div class="slide_item">2</div>
<div class="slide_item">3</div>
<div class="slide_item">4</div>
<div class="slide_item">5</div>
<div class="slide_prev_button slide_button">◀</div>
<div class="slide_next_button slide_button">▶</div>
</div>
<script src="./slide.js"></script>
</body>
</html>
3.2. CSS - slide.css
.slide {
/* layout */
display: flex;
flex-wrap: nowrap;
/* 컨테이너의 내용물이 컨테이너 크기(width, height)를 넘어설 때 보이지 않도록 하기 위해 hidden을 준다. */
overflow: hidden;
/* position */
/* slide_button의 position absolute가 컨테이너 안쪽에서 top, left, right offset이 적용될 수 있도록 relative를 준다. (기본값이 static인데, static인 경우 그 상위 컨테이너로 나가면서 현재 코드에선 html을 기준으로 offset을 적용시키기 때문) */
position: relative;
/* size */
width: 100%;
}
.slide_item {
/* layout */
display: flex;
align-items: center;
justify-content: center;
/* position - 버튼 클릭시 left offset값을 적용시키기 위해 */
position: relative;
left: 0px;
/* size */
width: 100%;
height: 300px;
/* flex item의 flex-shrink는 기본값이 1이므로 컨테이너 크기에 맞게 줄어드는데, 슬라이드를 구현할 것이므로 줄어들지 않도록 0을 준다. */
flex-shrink: 0;
/* transition */
transition: left 0.15s;
}
.slide_button {
/* layout */
display: flex;
justify-content: center;
align-items: center;
/* position */
position: absolute;
/* 버튼이 중앙에 위치하게 하기위해 계산 */
top: calc(50% - 16px);
/* size */
width: 32px;
height: 32px;
/* style */
border-radius: 100%;
background-color: #cccc;
cursor: pointer;
}
.slide_prev_button {
left: 10px;
}
.slide_next_button {
right: 10px;
}
/* 각 슬라이드가 변경되는 것을 시각적으로 확인하기 쉽도록 각 슬라이드별 색상 적용 */
.slide_item:nth-child(1) {
background-color: darkgoldenrod;
}
.slide_item:nth-child(2) {
background-color: aqua;
}
.slide_item:nth-child(3) {
background-color: blueviolet;
}
.slide_item:nth-child(4) {
background-color: burlywood;
}
.slide_item:nth-child(5) {
background-color: cornflowerblue;
}
3.3. JS - slide.js
// 슬라이크 전체 크기(width 구하기)
const slide = document.querySelector(".slide");
let slideWidth = slide.clientWidth;
// 버튼 엘리먼트 선택하기
const prevBtn = document.querySelector(".slide_prev_button");
const nextBtn = document.querySelector(".slide_next_button");
// 슬라이드 전체를 선택해 값을 변경해주기 위해 슬라이드 전체 선택하기
const slideItems = document.querySelectorAll(".slide_item");
// 현재 슬라이드 위치가 슬라이드 개수를 넘기지 않게 하기 위한 변수
const maxSlide = slideItems.length;
// 버튼 클릭할 때 마다 현재 슬라이드가 어디인지 알려주기 위한 변수
let currSlide = 1;
// 버튼 엘리먼트에 클릭 이벤트 추가하기
nextBtn.addEventListener("click", () => {
// 이후 버튼 누를 경우 현재 슬라이드를 변경
currSlide++;
// 마지막 슬라이드 이상으로 넘어가지 않게 하기 위해서
if (currSlide <= maxSlide) {
// 슬라이드를 이동시키기 위한 offset 계산
const offset = slideWidth * (currSlide - 1);
// 각 슬라이드 아이템의 left에 offset 적용
slideItems.forEach((i) => {
i.setAttribute("style", `left: ${-offset}px`);
});
} else {
currSlide--;
}
});
// 버튼 엘리먼트에 클릭 이벤트 추가하기
prevBtn.addEventListener("click", () => {
// 이전 버튼 누를 경우 현재 슬라이드를 변경
currSlide--;
// 1번째 슬라이드 이하로 넘어가지 않게 하기 위해서
if (currSlide > 0) {
// 슬라이드를 이동시키기 위한 offset 계산
const offset = slideWidth * (currSlide - 1);
// 각 슬라이드 아이템의 left에 offset 적용
slideItems.forEach((i) => {
i.setAttribute("style", `left: ${-offset}px`);
});
} else {
currSlide++;
}
});
// 브라우저 화면이 조정될 때 마다 slideWidth를 변경하기 위해
window.addEventListener("resize", () => {
slideWidth = slide.clientWidth;
});