현재 스크롤 되고 있는 부분에 맞춰서 헤더 제목이 포커싱되는 효과를 넣고 싶었다.
처음에는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
사실 내가 해결한 방식이 올바른 방식은 아닌 것 같긴하지만...
내가 원하는 화면 사이즈들에서는 정상적으로 작동하기때문에
일단은 저 방식을 사용했다.
추후에 더 좋은 방법을 찾게되면 수정할 예정이다.
문서가 수직으로 얼마나 스크롤됐는지 px 단위로 반환함
scrollY
와 pageYOffset
는 동일한 역할을 하지만,
일부 브라우저는 pageYOffset
만 지원하기도 하므로
pageYOffset
을 사용하는 것을 권장
수평 스크롤을 나타내는 pageXOffset
(=scrollX
)도 존재함
✨참고
https://developer.mozilla.org/ko/docs/Web/API/Window/pageYOffset
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
스크롤에 의해 가려진 분을 포함한 전체 문서 높이를 반환
✨참고
https://developer.mozilla.org/ko/docs/Web/API/Element/scrollHeight
https://ko.javascript.info/size-and-scroll-window
뷰포트 높이를 픽셀 단위로 반환
✨참고
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 {
}
});