본문 바로가기
Today I Learned/JS

[JS] SCROLL - 에이블런 프론트엔드부트캠프 22일차

by YES_developNewbie 2024. 8. 13.

JS에서 scroll 메서드를 활용해서 브라우저의 스크롤을 조정할 수 있다. 스크롤 메서드는 다음과 같이 작성한다.

// 1.
scroll(x-coord, y-coord)

// 2.
scroll({
	top: 0,
    left: 0,
   	behavior: 'smooth',
})

 

1번 코드, 2번코드는 동일하나, 2번 코드에서 behavior 옵션을 추가하여 'smooth'라고 작성했을 경우에는 부드럽게 화면 스크롤이 진행된다.

 

더보기
더보기

HTML

<!DOCTYPE html>
<html lang='ko'>
<head>
    <meta charset='UTF-8'>
    <title>Document</title>
    <!-- css -->
    <link rel='stylesheet' href='css/style.css'>
    <!-- script -->
    <script defer src="js/script.js"></script>
    <!-- fontawesome cdn-->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css">
</head>
<body>
    <section class="box1"></section>
    <section class="box2"></section>
    <section class="box3"></section>
    <section class="box4"></section>

    <ul class="btns">
        <li class="on"></li>
        <li></li>
        <li></li>
        <li></li>
    </ul>
</body>
</html>

CSS

// reset css import
@import "reset.scss";

section {
    width: 100%;
    height: 100vh;

    &.box1 {
        background: pink;
    }
    &.box2 {
        background: lightsalmon;
    }
    &.box3 {
        background: lightblue;
    }
    &.box4 {
        background: lightgreen;
    }
}
.btns {
    position: fixed;
    top: 50%;
    right: 50px;
    transform: translateY(-50%);

    >li {
        width: 20px;
        height: 20px;
        border-radius: 10px;
        background: #fff;
        margin-top: 20px;
        transition: 0.5s;

        &.on {
            background: #000;
            height: 50px;
        }
    }
}

스크롤 거리를 구한 뒤, 스크롤의 위치에 따라 버튼을 활성화하도록 코드를 구성했다.

const sections = document.querySelectorAll('section');
const btns = document.querySelectorAll('.btns li');

let posArr = [];
sections.forEach(section => {
    posArr.push(section.offsetTop);
})

window.addEventListener("scroll", () => {
    console.clear();

    // 스크롤 거리 구하기
    let scroll = window.scrollY;
    let base = 300;

    for (let i = 0; i < posArr.length; i++) {
        if (scroll >= posArr[i] &&
            (scroll < posArr[i+1] || i+1 == posArr.length)) {
            for (let btn of btns) {
                btn.classList.remove('on');
            }
            btns[i].classList.add('on');
        }
    }
})

 

하지만 posArr로 전역변수를 사용하는 것이 낭비라는 생각이 들었고, 나중에 HTML 코드를 추가하거나 하는 등의 상황이 발생했을 경우에 유연하지 못한 코드라고 생각했기 때문에 코드를 수정해주었다.

const sections = document.querySelectorAll('section');
const btns = document.querySelectorAll('.btns li');

window.addEventListener("scroll", () => {
    console.clear();

    // 스크롤 거리 구하기
    let scroll = window.scrollY;
    let base = 300;

    sections.forEach((section, idx) => {
        // section 안에 스크롤이 들어왔을 경우에만 실행
        if (scroll >= section.offsetTop - base &&
            (idx+1 == sections.length || scroll < sections[idx+1].offsetTop - base)) {
            for (let btn of btns) {
                btn.classList.remove('on');
            }
            btns[idx].classList.add('on');
        }
    });
})

 

posArr 변수를 삭제하였고, forEach를 통해 section 값과 index 값을 가져와 리팩토링했다.

// 버튼 클릭 이벤트 바인딩
btns.forEach((btn, idx) => {
    btn.addEventListener("click", (e) => {
        // 활성화된 버튼이 아닐 경우에만 실행
        let isOn = e.currentTarget.classList.contains('on');
        if (!isOn) {
            window.scroll({
                top: sections[idx].offsetTop,
                left: 0,
                behavior: 'smooth',
            });
        }
    });
})

 

 버튼들에 클릭이벤트를 바인딩하여 클릭했을 때 화면이 스크롤되어 버튼에 매칭되는 section이 보이도록 개발했다. 활성화된 버튼일 경우 실행되지 않아도 되기 때문에 예외처리를 해주었다.

// 리사이징 시 화면 재조정
window.addEventListener('resize', () => {
    const btnArr = Array.from(btns);
    let activeBtn = document.querySelector('.btns li.on');
    const activeIdx = btnArr.indexOf(activeBtn);

    console.log(activeIdx);

    window.scroll({
        top: sections[activeIdx].offsetTop,
        left: 0,
    })
})

 

사용자가 화면을 리사이징했을 경우, 활성화된 버튼과 화면에 보여지는 section이 매칭이 되지 않는 문제가 발생했다. 따라서 리사이징 이벤트 리스너를 따로 구현하여 재조정하는 코드를 작성해주었다.

// 휠 스크롤 시 실행
sections.forEach(((section, idx) => {
    section.addEventListener('mousewheel', (e) => {
        e.preventDefault();

        const sectionArr = Array.from(sections);
        let activeSection = document.querySelector('section.on');
        const activeIdx = sectionArr.indexOf(activeSection);

        // down -> 100  
        if (e.deltaY > 0) {
        	// 맨 아래일 경우 스킵
            if (idx == sections.length - 1) {
                return;
            }
            window.scroll({
                top: sections[activeIdx + 1].offsetTop,
                left: 0,
                behavior: 'smooth',
            });
        }
        // up -> -100
         else if (e.deltaY < 0) {
         	// 맨 위일 경우 스킵
            if (idx == 0) {
                return;
            }
            window.scroll({
                top: sections[activeIdx - 1].offsetTop,
                left: 0,
                behavior: 'smooth',
            });
        }
    })
}))

 

사용자가 휠 스크롤을 했을 경우에 자연스럽게 다음 section이 보여지도록 개발했다. 맨 위이거나 맨 아래일 때 예외처리를 해주었다.