[JS] Vanilla JS로 draggable slider 만들기

kysung95·2021년 5월 20일
21

Vanilla Js

목록 보기
1/1

안녕하세요. 김용성입니다.
지난 번에 말씀드렸듯이 저는 요즘 Vanilla Js만을 사용해서 웹페이지를 만드는 연습을 하고 있는데요. 외부 어떤 모듈도 사용하지 않고 만들어야겠다는 생각을 가지고 진행하고 있어요.
첫번째로 겪은 난관은 slider 구현이었는데요.
어제는 하루 온종일 이것만 붙잡고 있었던 것 같네요. 😂😂
draggable한 slider를 만드는 것에 대해서 한번 포스팅해보도록 하겠습니다.

결과물

Vanilla Js를 통해 이런식으로 drag가 가능한 slider를 만들려고 합니다.

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">
    <link rel="stylesheet" href="./style.css">
    <title>5.20 포스팅</title>
</head>

<body>
    <div class="title">
        <h1>YongSeong Velog</h1>
    </div>

    <div class="slider">
        <div class="slider-inner">
            <div class="slider-item"></div>
            <div class="slider-item"></div>
            <div class="slider-item"></div>
            <div class="slider-item"></div>
            <div class="slider-item"></div>
            <div class="slider-item"></div>
        </div>
    </div>
</body>
<script src="./index.js"></script>

</html>

이런식으로 구조를 잡아주었는데요.

slider라는 div 내부에 slider inner div를 생성해준 후, inner 요소의 style 중 left 요소를 통해서 드래그 가능하도록 구현을 할 예정입니다.

CSS

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.title {
  padding: 100px;
}

.slider {
  position: absolute;
  left: 10%;
  top: 30%;
  width: 80%;
  height: 200px;
  overflow: hidden;
}

.slider-inner {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 200%;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 5px;
  pointer-events: none;
}

.slider-item {
  height: 100%;
  background-color: black;
}

일단 left 요소를 적극 활용할 예정이므로 slider-inner div의 position을 absolute로 지정해주었고요. 그 내부에 slider item 들에 대해서는 grid 속성을 적용하였어요.
또한 slide가 가능하게끔 하기 위해 slider-inner의 width는 부모 태그인 slider div의 width보다 2배 크게 잡아주었고, overflow:hidden 속성을 사용하여 slider div에 해당하는 부분만 보여주게끔 해주었습니다.

✋ pointer-events는 무엇을 하는 녀석인가요??

네 이 녀석이 CSS에서의 핵심인데요.
pointer-events 값을 none으로 설정해주지 않은 경우에는 해당 요소에 대한 이벤트의 응답과 JS에서 요구하는 응답이 겹쳐지는 현상으로 인해 slider 제어가 엉망으로 되게 됩니다. 이 부분을 반드시 알아두세요! 쉽게 이해가 갈만한 사이트 링크를 함께 첨부하겠습니다.

CSS-이벤트 제어

JS

이제 가장 핵심인 JS 코드를 살펴보아야 합니다.
주요하게 사용한 메소드는 getBoundingClientRect()와 offsetX가 있습니다.

최종코드

//index.js

let slider = document.querySelector(".slider")
let innerSlider = document.querySelector(".slider-inner")
let pressed = false
let startx
let x

slider.addEventListener("mousedown", e => {
  pressed = true
  startx = e.offsetX - innerSlider.offsetLeft
  slider.style.cursor = "grabbing"
})

slider.addEventListener("mouseenter", () => {
  slider.style.cursor = "grab"
})

slider.addEventListener("mouseup", () => {
  slider.style.cursor = "grab"
})

window.addEventListener("mouseup", () => {
  pressed = false
})

slider.addEventListener("mousemove", e => {
  if (!pressed) return
  e.preventDefault()
  x = e.offsetX

  innerSlider.style.left = `${x - startx}px`
  checkboundary()
})

function checkboundary() {
  let outer = slider.getBoundingClientRect()
  let inner = innerSlider.getBoundingClientRect()

  if (parseInt(innerSlider.style.left) > 0) {
    innerSlider.style.left = "0px"
  } else if (inner.right < outer.right) {
    innerSlider.style.left = `-${inner.width - outer.width}px`
  }
}

이제 코드에 대한 설명을 드리겠습니다.

변수

let slider = document.querySelector(".slider")
let innerSlider = document.querySelector(".slider-inner")
let pressed = false
let startx
let x

우선 변수로는 다음과 같은 것들이 있는데요.

  • pressed : 클릭 상태 체크
  • startx : 마우스 드래그 시작점 x좌표 체크
  • x: 마우스 드래그시 x좌표 체크

이렇게 설명할 수 있습니다.

이벤트 등록

slider.addEventListener("mousedown", e => {
  pressed = true
  startx = e.offsetX - innerSlider.offsetLeft
  slider.style.cursor = "grabbing"
})

일단 slider에 다음과 같이 이벤트를 등록시켜주었어요.

offsetLeft는 offsetParent를 기준으로 각각 요소가 오른쪽으로 얼마나 떨어져 있는지를 나타냅니다.

마우스가 클릭 된 좌표에서 innerSlider의 현재 offsetLeft요소를 빼주는 식으로 startx가 업데이트 될거예요.
slider를 구현할 때 많이 사용하니까 알아두시면 정말 좋습니다.

slider.addEventListener("mouseenter", () => {
  slider.style.cursor = "grab"
})

slider.addEventListener("mouseup", () => {
  slider.style.cursor = "grab"
})

window.addEventListener("mouseup", () => {
  pressed = false
})

여기서 등록한 이벤트들은 모두다 마우스의 커서, 그리고 클릭 상태를 체크해주기 위한 이벤트들로, 이해하기 난해한 코드는 아니라고 생각하여 넘어가도록 하겠습니다.


function checkboundary() {
  let outer = slider.getBoundingClientRect()
  let inner = innerSlider.getBoundingClientRect()

  if (parseInt(innerSlider.style.left) > 0) {
    innerSlider.style.left = "0px"
  } else if (inner.right < outer.right) {
    innerSlider.style.left = `-${inner.width - outer.width}px`
  }
}

위 checkboundary()함수를 잘 보셔야 하는데요.
checkboundary()는 slider의 시작점과 끝점을 체크해서 boundary가 초과하지 않도록 해주는 함수예요.

우선 if문부터 설명하자면 innerSlicer.style.left값을 측정하여 0보다 클 경우에는 left값을 초기화 해주도록 하는 작업입니다. 만약 이 부분이 없다? slider가 오른쪽으로도 drag가 가능해질 것이기 때문에 이 부분을 처리해주었습니다.

else if문에 대해서는 getBoundingCliendRect()라는 메소드를 사용한 것을 보실 수 있을텐데요.
getBoundingCliendRect()메소드를 사용하면 해당 요소의 모든 좌표값을 받을 수가 있어요.
이런식으로 말이죠.

{
  bottom: 178
  height: 44
  left: 212.5
  right: 1092.5
  top: 134
  width: 880
  x: 212.5
  y: 134
}

이를 통해 바깥 slider와 내부 slider의 right값을 계산해서 외부 right값이 더 커지게 될 경우 더이상 slider drag가 진행되지 않도록 하는 것입니다.

slider.addEventListener("mousemove", e => {
  if (!pressed) return
  e.preventDefault()
  x = e.offsetX

  innerSlider.style.left = `${x - startx}px`
  checkboundary()
})

마지막으로 drag를 처리하는 가장 중요한 이벤트인데요.
innerSlider의 left값을 처리해줍니다. 위에서 설명드린 x값에서 startx을 빼주면 left값이 음수가 되겠죠? 그런식으로 slider를 진행해 나가고, 이 이벤트가 실행되는 즉시 위에서 만든 checkboundary() 함수를 호출하여 영역 내부 처리가 맞는지 확인을 해주는 것입니다.

🙌 마무리

간단한 구현일지 모르겠지만, 저는 많은 시간을 할애했습니다. 여전히 갈 길이 먼 것 같아요.😂😂
6월 안으로 Vanilla Js로 진행하는 프로젝트를 마감하고 싶은데, 잘 될지 모르겠네요.
그래도 Vanilla Js가 확실히 하나하나 만들어가는 맛이 있는 것 같아요.
읽어주셔서 감사합니다.

profile
김용성입니다.

1개의 댓글

comment-user-thumbnail
2021년 10월 26일

다른 유튜브,블로그 보다 정말 친절하게 알려주시는 거 같아요. Vanilla JS 이용한 예제가 이것 밖에 없다는 거에 참으로 아쉽습니다..ㅠㅠㅠ 유튭 하셔도 인기 많으실 거 같아요. 덕분에 드래그 슬라이더 잘 배웠습니다 감사합니다!!

답글 달기