[JS] 스크롤 감지 이벤트

MINBOK·2022년 3월 21일
0

Project

목록 보기
5/5
post-thumbnail

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

특정 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 {

    }
});

0개의 댓글