# Scrolling

window 나 scrollable elements 의 스크롤링에 반응하는 이벤트

현재 스크롤을 보여주는 함수

window.addEventListener('scroll', function() {
  document.getElementById('showScroll')
          .innerHTML = window.pageYOffset + 'px';
});
1
2
3
4

스크롤의 활용

  • 문서에서 사용자가 있는 위치에 따라 추가 컨트롤 또는 정보를 표시하거나 숨기는 기능
  • 사용자가 페이지 아래끝으로 스크롤하면 더 많은 데이터를 로드하는 기능

# scrolling 방지 방법

  • 스크롤 이벤트가 일어 난후 onscroll 리스너에 event.preventDefault() 를 한다.
  • CSS overflow 속성을 이용

# Endless page

# 스크롤의 두가지 중요한 특징

  1. elastic 탄력적

    • 일부 브라우저/디바이스 에서는, document 의 시작 또는 끝을 약간 넘어서 스크롤 할 수 있다.
    • 빈 공간이 표시가 되고 document 가 자동으로 bounces back 된다. bounce-back
  2. imprecise 부정확성

    • 페이지의 끝으로 스크롤 하였을 때, 실제 document 의 하단에서 0-50px 정도의 오차가 존재한다.

# Element.getBoundingClientRect()

DOMRect 요소의 크기 및 뷰포트를 기준으로 한 위치에 대한 정보를 제공 하는 객체를 반환합니다 .

뷰포트

  • 브라우저 용어에서, 현재 창 (또는 문서를 전체 화면 모드로 보는 경우 화면)에 표시되는 현재보고있는 문서의 부분을 나타냅니다.
  • 뷰포트 외부의 콘텐츠는 스크롤 할 때까지 화면에 표시되지 않습니다.

페이지가 아래로 스크롤 되는 것을 감지하는 방법

  • window 의 상대 좌표를 사용함

document.documentElement.getBoundingClientRect()

  • 전체 문서의 window 기준 좌표를 가져옴

image

전체 HTML document 의 높이가 2000px 일 때,

// 페이지의 top 에 있을 때

// window-relative top = 0
document.documentElement.getBoundingClientRect().top = 0

// window-relative bottom = 2000
// the document is long, so that is probably far beyond the window bottom
document.documentElement.getBoundingClientRect().bottom = 2000
1
2
3
4
5
6
7
8

500px 아래로 스크롤하면

// document top is above the window 500px
document.documentElement.getBoundingClientRect().top = -500

// document bottom is 500px closer
document.documentElement.getBoundingClientRect().bottom = 1500
1
2
3
4
5

창 높이를 600px 로 가정하고 끝까지 스크롤 할 때

// document top is above the window 1400px
document.documentElement.getBoundingClientRect().top = -1400

// document bottom is below the window 600px
document.documentElement.getBoundingClientRect().bottom = 600
1
2
3
4
5

bottom 프로퍼티는 절대 0 이 되지 않는다.

  • bottom 이 window 의 top 의 위치에 올 수 없기 때문

bottom 의 최소값이 되었을 때

  • 값 →창높이
  • 더이상 위로 스크롤 할 수 없는 상태

창높이

  • document.documentElement.clientHeight
  • 가로스크롤과 테두리를 제외한 창의 높이

document bottom 이 100px 으로부터 멀어지지 않는 때를 알야야 한다.

  • scroll 의 elastic 과 imprecise 특성 때문
  • 높이가 600px 인 경우, 600-700px 사이 이다.

예시

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
<h1>Scroll me</h1>
<script>
  function populate() {
    while(true) {
      let windowRelativeBottom = document.documentElement.getBoundingClientRect().bottom;
      if (windowRelativeBottom > document.documentElement.clientHeight + 100) break;
      document.body.insertAdjacentHTML("beforeend", `<p>Date: ${new Date()}</p>`);
    }
  }
  window.addEventListener('scroll', populate);
  populate(); // init document
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Up/down button

to the top button 예시

  1. 스크롤이 되고 있지 않을 때는 버튼이 나타나지 않는다.
  2. 스크롤이 window height 만큼 아래로 스크롤 되었을 때 버튼이 나타난다.
  3. 버튼이 있지만, 페이지를 2 번 상태 이전으로 스크롤 하면 버튼이 사라진다.
  4. 버튼을 누르면 페이지가 top 으로 스크롤 되고 벝튼이 사라진다.
<!DOCTYPE HTML>
<html>
<head>
  <style>
    body, html {
      height: 100%; width: 100%; padding: 0; margin: 0;
    }
    #matrix {
      width: 400px; margin: auto; overflow: auto; text-align: justify;
    }
    #arrowTop {
      height: 9px; width: 14px; color: green;
      position: fixed; top: 10px; left: 10px;
      cursor: pointer;
    }

    #arrowTop::before {
      content: '▲';
    }

  </style>
  <meta charset="utf-8">
</head>

<body>
  <div id="matrix">
    <script>
      for (let i = 0; i < 2000; i++) document.writeln(i)
    </script>
  </div>

  <div id="arrowTop" hidden></div>

  <script>
    arrowTop.onclick = function() {
      window.scrollTo(pageXOffset, 0);
      // after scrollTo, there will be a "scroll" event, so the arrow will hide automatically
    };

    window.addEventListener('scroll', function() {
      arrowTop.hidden = (pageYOffset < document.documentElement.clientHeight);
    });
  </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# Load visible images

  1. 이미지를 즉시 로드하지 않고, 자리 표시자로 대체한다.

    <img src="placeholder.svg" width="128" height="128" data-src="real.jpg">
    
    1
  2. 페이지가 사용자가 볼 수 있는 위치로 스크롤 하면, 변경되어 src 에 dara-src 이미지가 로드됨

예시 의 조건

  1. 페이지가 로드 될 때 화면에있는 이미지는 스크롤하기 전에 즉시로드되어야합니다.
  2. 일부 이미지는 data-src.
  3. 일단 이미지가 로드 되면 스크롤 인 / 아웃 할 때 더 이상 다시 로드 되지 않아야 합니다.
  4. 가능하다면 현재 위치보다 한 페이지 아래 / 뒤에있는 이미지를 "미리로드"하는 고급 솔루션을 만드십시오.
  5. 세로 스크롤 만 처리하고 가로 스크롤은 처리하지 않습니다.

요소(이미지)가 사용자에게 보이는 위치인지 검사한다.

function isVisible(elem) {
  let coords = elem.getBoundingClientRect();
  let windowHeight = document.documentElement.clientHeight;

  // top elem edge is visible OR bottom elem edge is visible
  let topVisible = coords.top > 0 && coords.top < windowHeight;
  let bottomVisible = coords.bottom < windowHeight && coords.bottom > 0;
  return topVisible || bottomVisible;
}

/**
A variant 
function isVisible(elem) {
  let coords = elem.getBoundingClientRect();
  let windowHeight = document.documentElement.clientHeight;

  let extendedTop = -windowHeight;
  let extendedBottom = 2 * windowHeight;

  // top visible || bottom visible
  let topVisible = coords.top > extendedTop && coords.top < extendedBottom;
  let bottomVisible = coords.bottom < extendedBottom && coords.bottom > extendedTop;

  return topVisible || bottomVisible;
}
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function showVisible() {
  for (let img of document.querySelectorAll('img')) {
    let realSrc = img.dataset.src;
    if (!realSrc) continue;

    if (isVisible(img)) {
      // disable caching
      // this line should be removed in production code
      realSrc += '?nocache=' + Math.random();

      img.src = realSrc;

      img.dataset.src = '';
    }
  }

}

window.addEventListener('scroll', showVisible);
showVisible();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20