[JS] 스크롤 감지 이벤트

현재 스크롤 되고 있는 부분에 맞춰서 헤더 제목이 포커싱되는 효과를 넣고 싶었다.

특정 Element의 절대 요소 구하기

처음에는pageYOffset의 값(이하 posY)이 특정 숫자 범위안에 있을때, 각각의 제목이 포커싱 되도록 했다.

위 사진으로 예를 들자면,
posY가 0<= posY < 100의 값이면 헤더의 home이 포커싱 되고 100<= posY < 200이면 about이 포커싱되는 방식이었다.

이 방식은 화면 크기가 변할때 마다 값이 달라졌기 때문에 반응형 웹에 적합하지않았다.

이 문제를 해결하기 위해 각 부분을 하나로 묶어주고 각 부분의 상단을 기준으로 삼기로했다.

예를 들자면,
posY가 #home의 최상단 <= posY < #about의 최상단에
위치하면 home이 포커싱되도록 만들기로 했다.

<body>
  <header id="header"></header>
  <section id="home"></section>
  <section id="about"></section>
  <section id="skills"></section>
  <section id="work"></section>
  <footer id="footer"></footer>
</body>

이를 위해 요소의 절대좌표가 필요했는데, 요소의 절대좌표는 아래의 방식으로 구할 수 있었다.

const absoluteTop = window.pageYOffset + element.getBoundingClientRect().top;

출처: https://mommoo.tistory.com/85 [개발자로 홀로 서기]

이 방식에도 문제가 있었는데, 최하단에 위치한footer와 그 위의 요소가 함께 포커싱되는 문제가 발생했다.

이러한 문제는 스크롤이 최하단에 위치할 때, footer(contact 부분)가 포커싱되게 하는 것으로 해결했다.

스크롤이 끝까지 스크롤 되었는지 판별하기

element.scrollHeight - element.scrollTop === element.clientHeight

출처: https://developer.mozilla.org/ko/docs/Web/API/Element/scrollHeight

위의 식이 참이면 스크롤이 끝까지 스크롤 된 것이다.
구글 검색을 통해 찾은 다른 블로그들에서도 위와 같은 방식으로 사용하고있었다.

나는 posY >= workTop && posY <= totalHeight
totalHeight 부분을 채워넣고싶었기 때문에 숫자값이 필요했다.

스크롤이 최하단에 위치할 때, scrollHeight의 값이 innerHeight 값과 거의 같다는 사실을 이용하기로 했다.
(화면 크기에 따라 오차범위 존재함 0 ~ 1)

let totalHeight = document.body.scrollHeight - this.window.innerHeight -1;

else if(posY >= workTop && posY <= totalHeight) {
  
} else {
  
}

totalHeight

사실 내가 해결한 방식이 올바른 방식은 아닌 것 같긴하지만...
내가 원하는 화면 사이즈들에서는 정상적으로 작동하기때문에
일단은 저 방식을 사용했다.
추후에 더 좋은 방법을 찾게되면 수정할 예정이다.

Window.pageYOffset

문서가 수직으로 얼마나 스크롤됐는지 px 단위로 반환

scrollYpageYOffset는 동일한 역할을 하지만,
일부 브라우저는 pageYOffset만 지원하기도 하므로
pageYOffset을 사용하는 것을 권장

수평 스크롤을 나타내는 pageXOffset(=scrollX)도 존재함

참고
https://developer.mozilla.org/ko/docs/Web/API/Window/pageYOffset

Element.getBoundingClientRect()

viewport를 기준으로 상대적인 위치 정보를 반환하는 메서드.
내가 사용한 프로퍼티는 top인데, 이는 요소를 감싸는 네모의 위쪽 모서리, Y 좌표를 반환한다.

* 뷰포트(viewport): 현재 화면에 보여지는 영역

아래 이미지 기준에서 pageX,Y는 pageYOffset 기준 좌표,
clientX, Y는 getBoundingClientRect().top 기준 좌표임


이미지 출처: https://ko.javascript.info/coordinates

참고
https://developer.mozilla.org/ko/docs/Web/API/Element/getBoundingClientRect

https://ko.javascript.info/coordinates

Element.scrollHeight

스크롤에 의해 가려진 분을 포함한 전체 문서 높이를 반환

참고
https://developer.mozilla.org/ko/docs/Web/API/Element/scrollHeight

https://ko.javascript.info/size-and-scroll-window

Window.innerWidth

뷰포트 높이를 픽셀 단위로 반환

참고
https://developer.mozilla.org/en-US/docs/Web/API/Window/innerHeight

최종 완성 코드

HTML

<body>
  <header id="header"></header>
  <section id="home"></section>
  <section id="about"></section>
  <section id="skills"></section>
  <section id="work"></section>
  <footer id="footer"></footer>
</body>

CSS

.focus {
    color: #ECECEC;
    border: 2.75px solid #ECECEC;
    border-radius: 10px;
}

JavaScript

// header for desktop

window.addEventListener('scroll', function() {
    const posY = this.window.pageYOffset;
    const home = this.document.querySelector('#home').getBoundingClientRect().top;
    const about = this.document.querySelector('#about').getBoundingClientRect().top;
    const skills = this.document.querySelector('#skills').getBoundingClientRect().top;
    const work = this.document.querySelector('#work').getBoundingClientRect().top;

    const homeTop = posY + home;
    const aboutTop = posY + about;
    const skillsTop = posY + skills;
    const workTop = posY + work;

    let totalHeight = document.body.scrollHeight - this.window.innerHeight -1;

    if(posY >= homeTop && posY < aboutTop) {

    } else if(posY >= aboutTop && posY < skillsTop) {

    } else if(posY >= skillsTop && posY < workTop) {

    } else if(posY >= workTop && posY <= totalHeight) {

    } else {

    }
});

좋은 웹페이지 즐겨찾기