29 січня 2022 р.

Прокрутка

Подія scroll дозволяє реагувати на прокручування сторінки або елемента. Є багато цікавих речей, які при цьому можна зробити.

Наприклад:

  • Показати/приховати додаткові елементи керування або інформацію залежно від того, де в документі перебуває користувач.
  • Завантажити більше даних, коли користувач прокрутить сторінку вниз до кінця.

Ось невелика функція для відображення поточного прокручування:

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

У дії:

Поточна прокрутка = прокрутіть вікно браузера

Подія scroll працює як на window, так і на елементах, які можна прокручувати.

Запобігання прокручування

Як ми можемо зробити щось непрокручуваним?

Ми не можемо запобігти прокручуванню за допомогою event.preventDefault() у прослуховувачі onscroll, оскільки він запускається після прокручування.

Але ми можемо запобігти прокручуванню за допомогою event.preventDefault() для події, яка його викликає, наприклад, події keydown для pageUp та pageDown.

Якщо ми додамо обробник до цих подій з event.preventDefault(), то прокручування не почнеться.

Існує багато способів ініціювати прокручування, тому надійніше використовувати CSS, а саме властивість overflow.

Ось кілька завдань, які ви можете вирішити або переглянути, щоб побачити застосування onscroll.

Завдання

важливість: 5

Створіть нескінченну сторінку. Коли відвідувач прокручує її до кінця, поточна дата-час автоматично додається до тексту (щоб відвідувач міг прокручувати більше).

ось таким чином:

Будь ласка, зверніть увагу на дві важливі особливості прокрутки:

  1. Прокручування є “еластичним”. У деяких браузерах/пристроях ми можемо прокрутити трохи далі початку або кінця документа (буде показано пусте місце, а потім документ автоматично “повернеться” до нормального стану).
  2. Прокрутка неточна. Коли ми прокручуємо до кінця сторінки, насправді ми можемо бути на відстані 0-50 пікселів від реального низу документа.

Отже, “прокрутка до кінця” має означати, що відвідувач знаходиться на відстані не більше 100 пікселів від кінця документа.

P.S. У реальному житті ми можемо захотіти показати “більше повідомлень” або “більше товарів”.

Відкрити пісочницю для завдання.

Основою рішення є функція, яка додає більше дат на сторінку (або завантажує більше речей у реальному житті), поки ми знаходимося в кінці сторінки.

Ми можемо негайно викликати її та додати window.onscroll як обробник.

Найважливіше запитання: “Як ми виявимо, що сторінка прокручується вниз?”

Скористаємось координатами, відносними до вікна.

Документ представлений (і міститься) у тегу <html>, тобто document.documentElement.

Ми можемо отримати відносні до вікна координати всього документа як document.documentElement.getBoundingClientRect(), властивість bottom буде відносною до вікна координатою низа документа.

Наприклад, якщо висота всього HTML-документа дорівнює 2000px, тоді:

// коли ми знаходимося вгорі сторінки
// top відносний до вікна дорівнює 0
document.documentElement.getBoundingClientRect().top = 0

// bottom відносний до вікна дорівнює 2000
// документ великий, тому він, ймовірно, знаходиться далеко за нижньою частиною вікна
document.documentElement.getBoundingClientRect().bottom = 2000

Якщо ми прокрутимо 500px нижче, то:

// Верхня частина документа знаходиться над вікном на 500px
document.documentElement.getBoundingClientRect().top = -500
// Нижня частина документа на 500px ближче
document.documentElement.getBoundingClientRect().bottom = 1500

Коли ми прокручуємо до кінця, припускаючи, що висота вікна дорівнює 600px:

// Верхня частина документа знаходиться над вікном на 1400px
document.documentElement.getBoundingClientRect().top = -1400
// Нижня частина документа знаходиться під вікном на 600px
document.documentElement.getBoundingClientRect().bottom = 600

Зауважте, що bottom не може бути 0, оскільки він ніколи не досягає верхньої частини вікна. Найнижча межа bottom координати – це висота вікна (ми припустили, що це 600), ми більше не можемо прокручувати вгору.

Ми можемо отримати висоту вікна як document.documentElement.clientHeight.

Для нашого завдання нам потрібно знати, коли нижня частина документа знаходиться на відстані не більше ніж 100px від неї (тобто: 600-700px, якщо висота 600).

Отже, ось функція:

function populate() {
  while(true) {
    // нижня частина документа
    let windowRelativeBottom = document.documentElement.getBoundingClientRect().bottom;

    // якщо користувач не прокрутив достатньо далеко (>100px до кінця)
    if (windowRelativeBottom > document.documentElement.clientHeight + 100) break;

    // додамо більше даних
    document.body.insertAdjacentHTML("beforeend", `<p>Date: ${new Date()}</p>`);
  }
}

Відкрити рішення в пісочниці.

важливість: 5

Створіть кнопку “вгору”, щоб допомогти прокручувати сторінку.

Вона має працювати так:

  • Поки сторінка не прокручена принаймні на висоту вікна – вона невидима.
  • Якщо сторінка прокручена нижче висоти вікна, у верхньому лівому куті з’являється стрілка “вгору”. Якщо сторінку прокрутити назад, вона зникне.
  • При натисканні стрілки сторінка прокручується вгору.

Ось так (верхній лівий кут, прокрутіть, щоб побачити):

Відкрити пісочницю для завдання.

важливість: 4

Припустимо, у нас є клієнт з низькою швидкістю з’єднання, і ми хочемо заощадити його мобільний трафік.

З цією метою ми вирішуємо не показувати зображення відразу, а замінити їх заповнювачами, наприклад:

<img src="placeholder.svg" width="128" height="128" data-src="real.jpg">

Отже, спочатку всі зображення є placeholder.svg. Коли сторінка прокручується до місця, де користувач може побачити зображення, ми змінюємо src на значення data-src, і таким чином зображення завантажується.

Ось приклад в iframe:

Прокрутіть, щоб побачити, як зображення завантажуються “на вимогу”.

Вимоги:

  • Коли сторінка завантажується, зображення на екрані мають завантажуватися негайно, перед будь-яким прокручуванням.
  • Деякі зображення можуть бути звичайними, без data-src. Код не повинен їх торкатися.
  • Після того, як зображення завантажено, воно більше не повинно перезавантажуватися під час прокручування/виходу.

P.S. Якщо ви можете, створіть більш просунуте рішення, яке б “попередньо завантажувало” зображення, які знаходяться на одну сторінку нижче/після поточної позиції.

P.P.S. Потрібно обробляти лише вертикальну прокрутку, горизонтально прокручувати не можна.

Відкрити пісочницю для завдання.

Обробник onscroll повинен перевірити, які зображення є видимими, і показати їх.

Ми також хочемо запускати його під час завантаження сторінки, щоб виявляти видимі зображення відразу та завантажувати їх.

Код повинен виконуватися під час завантаження документа, щоб він мав доступ до його вмісту.

Або розмістіть його внизу <body>:

// ...вміст сторінки вище...

function isVisible(elem) {

  let coords = elem.getBoundingClientRect();

  let windowHeight = document.documentElement.clientHeight;

  // видно верхній край елемента?
  let topVisible = coords.top > 0 && coords.top < windowHeight;

  // видно нижній край елемента?
  let bottomVisible = coords.bottom < windowHeight && coords.bottom > 0;

  return topVisible || bottomVisible;
}

Функція showVisible() використовує перевірку видимості, реалізовану isVisible(), для завантаження видимих зображень:

function showVisible() {
  for (let img of document.querySelectorAll('img')) {
    let realSrc = img.dataset.src;
    if (!realSrc) continue;

    if (isVisible(img)) {
      img.src = realSrc;
      img.dataset.src = '';
    }
  }
}

showVisible();
window.onscroll = showVisible;

P.S. Рішення також має варіант isVisible, який “попередньо завантажує” зображення, які знаходяться в межах 1 сторінки вище/під поточною прокруткою документа.

Відкрити рішення в пісочниці.

Навчальна карта

Коментарі

прочитайте це, перш ніж коментувати…
  • Якщо у вас є пропозиції, щодо покращення підручника, будь ласка, створіть обговорення на GitHub або одразу створіть запит на злиття зі змінами.
  • Якщо ви не можете зрозуміти щось у статті, спробуйте покращити її, будь ласка.
  • Щоб вставити код, використовуйте тег <code>, для кількох рядків – обгорніть їх тегом <pre>, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)