[CodingApple] 스크롤 이벤트(Scroll Event)

Nadia·2024년 2월 27일
0

CodingApple

목록 보기
12/20
post-thumbnail
post-custom-banner



스크롤 이벤트(Scroll Event)



스크롤(scroll) 이벤트 리스너

: 스크롤(scroll) 시 발생하는 이벤트

 window.addEventListener('scroll', function() {
		 			속성
});
  • window: html 페이지

  • window.scrollY
    : 전체 페이지에서 스크롤바를 얼만큼 내렸는지 숫자로 출력

  • window.scrollX
    : 가로 스크롤

  • window.scrollTo(x, y)
    : 원하는 좌표로 강제로 스크롤 변경

  • window.scrollBy(x, y)
    : 현재 위치에서부터 강제로 스크롤하기

window.addEventListener('scroll', function() {
    window.scrollTo(0, 100);
});

jquery 버전

  • scrollTop()
    : 현재 스크롤바 위치 출력 (위에서 얼마나 내렸는지)
$(window).on('scroll', function() {
    $(window).scrollTop(100)
	// 현재 위치에서 y축으로 100만큼 이동
})



<div> 상자에서의 스크롤

  • .scrollTop : 특정 <div> 상자에서 스크롤바를 내린 양
    - 스크롤바 내린 양은 진짜 스크롤바 내린 양일 뿐,
    현재 화면에 보이는 박스의 높이는 포함 X
  • .clientHeight : 현재 화면에 보이는 <div> 상자의 높이

  • .scrollHeight : 특정 <div> 상자의 전체 높이

document.querySelector('.lorem').addEventListener('scroll', function() {

  var 스크롤양 = document.querySelector('.lorem').scrollTop; // 188
  var 높이 = document.querySelector('.lorem').clientHeight; // 100
  var 실제높이 = document.querySelector('.lorem').scrollHeight; // 288



전체 페이지 <html>의 스크롤

  • document.querySelector('html').scrollHeight : html(전체 페이지)의 높이
    (= document.documentElement.scrollHeight)

  • document.querySelector('html').clientHeight; : 현재 화면에 보이는 부분의 높이

  • window.screenY : 현재 페이지의 스크롤 높이



CSS

  • overflow-x: scroll; : 세로 스크롤 만들기
    overflow-y: scroll; : 가로 스크롤 만들기

  • position: fixed; : 상단 고정 (스크롤 되어도 상단에 고정됨)


  • 스크롤이 부드럽게 작동하는 것을 막기
    :root {
        scroll-behavior: auto;
    }




스크롤(scroll) 이벤트 주의점!

  1. scroll 이벤트 리스너 안의 코드는 1초에 60번 이상 실행됨

    • 많이 달면 성능 저하 발생, 스크롤바 1개마다 1개만 사용
    • 바닥 체크하는 코드가 중복 실행됨
  2. scrollHeight 구할 땐 브라우저마다 아주 약간의 오차가있을 수 있어서 테스트 해보는게 좋다.

  3. scrollHeight 구하는 코드는 페이지 로드가 완료되고나서 실행해야 정확하기 때문에 <body> 끝나기 전에 적는게 좋다.


스크롤(scroll) 이벤트 중복 제거하기 *

  1. .removeEventListener('scroll', arguments.callee);
  • .removeEventListener() : 추가한 이벤트를 제거하는 함수
    - 필요한 요소:
    1. 제거할 엘리먼트(element) 요소
    2. 이벤트 타입 (click, scroll, keypress 등)
    3. 제거할 이벤트의 callback 함수

  • arguments.callee : 현재 실행 중인 함수를 참조할 수 있는 속성
    - callback 함수가 없는 경우(= 익명 함수) 이벤트를 제거 할 수 있다. 이때 이벤트 함수의 콜백 함수는 반드시 function 키워드로 작성해야 한다.


  1. boolean 변수 사용하기
    = '위로 올렸다가 다시 스크롤하면 alert창 계속 띄우기' 참고



위로 올렸다가 다시 스크롤하면 alert창 계속 띄우기

  1. boolean형 변수를 선언 let hasScrolled = false;
  2. alert 창 띄우는 조건이 만족하는 경우 true로 변경
  3. 스크롤 위치가 보여지는 <div> 상자의 높이보다 작으면 다시 false로 변경




예제


1. 스크롤바 내리면 로고 작게 만들기

  • 스크롤바 100px 이상 내리면 로고 폰트 사이즈 줄이기
window.addEventListener('scroll', function() {
// 스크롤바가 움직일 때마다 실행되어야 하기 때문에 이벤트리스너 사용

  if (window.scrollY > 100) {
     document.getElementsByClassName('navbar-brand')[0].style.fontSize = '20px'
  } else if (window.scrollY < 100) {
     document.getElementsByClassName('navbar-brand')[0].style.fontSize = '25px'
  }
});




2. 약관 끝까지 읽으면 alert 띄우기

1. <div> 상자 바닥 체크하기

if (div 상자에서 스크롤바 내린 높이 + 현재 화면에 보이는 div 상자의 높이 > div 상자의 실제 높이 - 10) {
   //   .scrollTop  +  .clientHeight  >  .scrollHeight - 10
     alert()
}

2. 소수점 때문에 오차가 발생할 수 있기 때문에
끝까지 스크롤한 것보다는 끝에서 10px 정도 남기고 스크롤 했는지 검사하기

3. 위로 올렸다가 다시 스크롤하면 alert창 계속 띄우기
let hasScrolled = false;

const lorem = document.querySelector('.lorem');
let hasScrolled = false;

lorem.addEventListener('scroll', function() {
    
    let scrollLength = lorem.scrollTop;     // 스크롤 끝까지 내린 위치
    let loremCHeight = lorem.clientHeight;  // 보여지는 div 높이
    let loremSHeight = lorem.scrollHeight;  // 콘텐츠 전체 높이

    if ((scrollLength + loremCHeight) >= (loremSHeight - 10) && !hasScrolled) {
        alert('약관을 확인했습니다.');
        hasScrolled = true;
      }

    // 위로 올렸다가 다시 스크롤하면 alert창 계속 띄우기
    if (scrollLength < loremCHeight) {  // 스크롤 위치(scrollLength)가 보여지는 약관 div 높이(loremCHeight)보다 작으면
        hasScrolled = false;
    }  
});
  }




3. 현재 페이지의 끝까지 스크롤 체크하기

1. <div> 상자 바닥 체크하기
2. 위로 올렸다가 다시 스크롤하면 alert창 계속 띄우기

let hasScrolled = false;

window.addEventListener('scroll', function() {
    var loremSHeight = document.querySelector('html').scrollHeight; // html(전체 페이지)의 높이
    var loremCHeight = document.querySelector('html').clientHeight; // 현재 화면에 보이는 부분의 높이
    var scrollLength = document.querySelector('html').scrollTop; // 현재 페이지의 스크롤양

    if ((scrollLength + loremCHeight) > (loremSHeight - 5) && !hasScrolled) {
        alert('페이지를 모두 확인했습니다.')
        hasScrolled = true;
    }

    // 위로 올렸다가 다시 스크롤하면 alert창 계속 띄우기
    if (scrollLength < loremCHeight) {  // 스크롤 위치(scrollLength)가 보여지는 약관 div 높이(loremCHeight)보다 작으면
        hasScrolled = false;
    }  
});




4. 상단의 진행바 만들기

  • 전체 세로폭 중 현재까지 스크롤한 값을 백분률 환산
    (브라우저 최하단까지 스크롤을 내리면 100%가 되도록)

case 1. 백분률로 바로 진행바 길이 조정하기

  • 깔끔하고 확장성 있는 코드
  • 그러나 UI가 이상하게 나온다.
    (스크롤을 움직이지 않은 페이지 상단에서도 진행바가 이미 15%정도 진행되어 있음)
const progress = ((scrollLength + loremCHeight) / loremSHeight) * 100;

document.querySelector('.ui').style.width = progress + '%';

case 2. 백분률을 직접 나눠서 진행바 길이 부여하기

const progress = ((scrollLength + loremCHeight) / loremSHeight) * 100;

if (progress < 16) {
  this.document.querySelector('.ui').style.width = '0%';
} else if (progress < 20) {
  this.document.querySelector('.ui').style.width = '20%';
} else if (progress < 40) {
  this.document.querySelector('.ui').style.width = '40%';
} else if (progress < 60) {
  this.document.querySelector('.ui').style.width = '60%';
} else if (progress < 80) {
  this.document.querySelector('.ui').style.width = '80%';
} else if (progress < 95) {
  this.document.querySelector('.ui').style.width = '95%';
} else if (progress > 97) {
  this.document.querySelector('.ui').style.width = '100%';
}




index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <!-- Bootstrap CSS -->
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
      crossorigin="anonymous"
    />
    <link href="main.css" rel="stylesheet" />
    <title>Hello, world!</title>
  </head>

  <body>
    <!-- 진행바 -->
    <div class="ui"></div>
    
    <nav class="navbar navbar-light bg-light">
      <div class="container-fluid">
        <span class="navbar-brand">Question</span>
        <button class="navbar-toggler" type="button">
          <!--Navbar 버튼-->
          <span class="navbar-toggler-icon"></span>
        </button>
      </div>
    </nav>

    <!-- 걍 스크롤이 필요해서 만든 큰 박스임 -->
    <div style="height: 2000px"></div>

    <div class="lorem" style="width: 200px; height: 100px; overflow-y: scroll;">
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quae voluptas voluptatum minus praesentium fugit debitis at, laborum ipsa itaque placeat sit, excepturi eius. Nostrum perspiciatis, eligendi quae consectetur praesentium exercitationem.
    </div>

    <script src="index.js"></script>
    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
      crossorigin="anonymous"
    ></script>
  </body>
</html>

index.js

// 1. 스크롤바 내리면 로고 폰트 작게 만들기
// - 스크롤바 100px 이상 내리면 로고 폰트 사이즈 줄이기
window.addEventListener('scroll', function() {

    if (window.scrollY > 100) {
        document.getElementsByClassName('navbar-brand')[0].style.fontSize = '20px'
    } else if (window.scrollY < 100) {
        document.getElementsByClassName('navbar-brand')[0].style.fontSize = '25px'
    }


    // 3. 현재 페이지의 끝까지 스크롤 체크하기
    // + alert창 계속 띄우기
    var loremSHeight = document.querySelector('html').scrollHeight; // html(전체 페이지)의 높이
    var loremCHeight = document.querySelector('html').clientHeight; // 현재 화면에 보이는 부분의 높이
    var scrollLength = document.querySelector('html').scrollTop; // 현재 페이지의 스크롤양

    if ((scrollLength + loremCHeight) > (loremSHeight - 5) && !hasScrolled) {
        alert('페이지를 모두 확인했습니다.')
        hasScrolled = true;
    }

    // 위로 올렸다가 다시 스크롤하면 alert창 계속 띄우기
    if (scrollLength < loremCHeight) {  // 스크롤 위치(scrollLength)가 보여지는 약관 div 높이(loremCHeight)보다 작으면
        hasScrolled = false;
    }
  

    // 4. 상단의 진행바 만들기
    // 전체 세로폭 중 현재까지 스크롤한 값을 백분률 환산
    // (브라우저 최하단까지 스크롤을 내리면 100%가 됨)
  
    // 방법 1. 백분률에 %를 붙여서 바로 진행바 길이 조정하기
    const progress = ((scrollLength + loremCHeight) / loremSHeight) * 100;
    document.querySelector('.ui').style.width = progress + '%';
    
    // 방법 2. 백분률을 직접 나눠서 길이 부여하기
    if (progress < 16) {
        this.document.querySelector('.ui').style.width = '0%';
    } else if (progress < 20) {
        this.document.querySelector('.ui').style.width = '20%';
    } else if (progress < 40) {
        this.document.querySelector('.ui').style.width = '40%';
    } else if (progress < 60) {
        this.document.querySelector('.ui').style.width = '60%';
    } else if (progress < 80) {
        this.document.querySelector('.ui').style.width = '80%';
    } else if (progress < 95) {
        this.document.querySelector('.ui').style.width = '95%';  
    } else if (progress > 97) {
        this.document.querySelector('.ui').style.width = '100%';
    }
});
        

// 2. 회원 약관 끝까지 읽으면 alert 띄우기
// + alert창 계속 띄우기
const lorem = document.querySelector('.lorem');
let hasScrolled = false;

lorem.addEventListener('scroll', function() {
    
    let scrollLength = lorem.scrollTop;     // 스크롤 끝까지 내린 위치
    let loremCHeight = lorem.clientHeight;  // 보여지는 div 높이
    let loremSHeight = lorem.scrollHeight;  // 콘텐츠 전체 높이

    if ((scrollLength + loremCHeight) >= (loremSHeight - 10) && !hasScrolled) {
        alert('약관을 확인했습니다.');
        hasScrolled = true;
      }

    // 위로 올렸다가 다시 스크롤하면 alert창 계속 띄우기
    if (scrollLength < loremCHeight) {  // 스크롤 위치(scrollLength)가 보여지는 약관 div 높이(loremCHeight)보다 작으면
        hasScrolled = false;
    }  
});

main.css

.navbar {
    position: fixed;
    /* 상단 고정(스크롤 되어도 상단에 고정됨) */
    width: 100%;
    z-index: 5;
}

/* 스크롤바 내리면 로고 작게 만들기 */
.navbar-brand {
    font-size: 25px;
    /* 시작 스타일: 애니메이션 작동 전의 글씨 크기 */
    transition: all 2s;
}

.small {
    font-size: 20px;
}

.show {
	display: block; 
}

.ui {
    background-color: red;
    padding: 1px;
    width: 0%;
    position: fixed;
    z-index: 10;
    transition: all 1s;
}


공부

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



출처
코딩애플
https://ko.javascript.info/size-and-scroll-window
https://developer.mozilla.org/ko/docs/Web/API/Element/scrollHeight
https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%A0%9C%EA%B1%B0-%ED%95%9C%EB%B2%88%EB%A7%8C-%EC%8B%A4%ED%96%89%EB%90%98%EA%B2%8C-%ED%95%98%EA%B8%B0-removeEventListener-once
https://kincoding.com/entry/%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%8A%A4%ED%81%AC%EB%A1%A4-%EC%A7%84%ED%96%89%EB%A5%A0%EC%9D%84-%ED%91%9C%EC%8B%9C%ED%95%B4%EB%B3%B4%EC%9E%90-%EC%83%81%EB%8B%A8-progress-bar-%EB%A7%8C%EB%93%A4%EA%B8%B0

profile
비전공자 개발 일기
post-custom-banner

0개의 댓글