← Назад к вопросам
Как отловить событие прокрутки в JavaScript?
1.7 Middle🔥 191 комментариев
#JavaScript Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
События прокрутки (Scroll events)
Событие прокрутки позволяет отловить момент, когда пользователь прокручивает страницу или элемент. Это критично для создания интерактивных интерфейсов — от ленивой загрузки изображений до бесконечного скролла.
Событие scroll на window
Самый простой способ — добавить слушатель события scroll на объект window:
// Базовый пример
window.addEventListener('scroll', function() {
console.log('Страница прокручена!');
console.log('Прокручено вверх на:', window.scrollY, 'пиксель');
console.log('Видимое окно:', window.innerHeight, 'пиксель');
});
Позиция скролла
Для определения позиции скролла используются свойства:
window.addEventListener('scroll', () => {
// window.scrollY или window.pageYOffset — вертикальная позиция
console.log('Вертикальный скролл:', window.scrollY);
// window.scrollX или window.pageXOffset — горизонтальная позиция
console.log('Горизонтальный скролл:', window.scrollX);
// Высота видимой области
console.log('Высота окна:', window.innerHeight);
// Общая высота документа
console.log('Высота документа:', document.documentElement.scrollHeight);
// Расстояние до конца страницы
const distanceToBottom = document.documentElement.scrollHeight - (window.scrollY + window.innerHeight);
console.log('До конца страницы:', distanceToBottom);
});
Проверка, прокручена ли страница вниз
window.addEventListener('scroll', () => {
// Проверить, достигнут ли конец страницы
const isAtBottom = window.scrollY + window.innerHeight >= document.documentElement.scrollHeight - 10;
if (isAtBottom) {
console.log('Пользователь достиг конца страницы!');
// Загрузить ещё контент
}
});
Ленивая загрузка изображений (Infinite Scroll)
window.addEventListener('scroll', () => {
const distanceToBottom = document.documentElement.scrollHeight - (window.scrollY + window.innerHeight);
if (distanceToBottom < 500) {
loadMoreContent();
}
});
function loadMoreContent() {
console.log('Загружаю больше контента...');
// Fetch запрос для получения новых данных
fetch('/api/items?offset=20')
.then(res => res.json())
.then(data => renderItems(data));
}
Оптимизация производительности (Throttle/Debounce)
Событие scroll срабатывает очень часто (60+ раз в секунду), что может замедлить приложение. Используй debounce или throttle:
// Простой throttle
function throttle(func, limit) {
let lastCall = 0;
return function(...args) {
const now = Date.now();
if (now - lastCall > limit) {
lastCall = now;
func.apply(this, args);
}
};
}
const handleScroll = throttle(() => {
console.log('Оптимизированный скролл:', window.scrollY);
}, 200); // срабатывает максимум раз в 200мс
window.addEventListener('scroll', handleScroll);
Scroll на конкретном элементе
Не только страница, но и отдельные элементы могут иметь прокрутку:
const container = document.querySelector('.scrollable-box');
container.addEventListener('scroll', () => {
console.log('Скролл контейнера:', container.scrollTop);
console.log('Ширина контейнера:', container.scrollLeft);
console.log('Полная высота:', container.scrollHeight);
// Проверить конец скролла в контейнере
const isAtBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 10;
if (isAtBottom) {
console.log('Конец контейнера!');
}
});
Плавная прокрутка к элементу
// Способ 1: scrollIntoView (современный)
const element = document.querySelector('#target');
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
// Способ 2: Программно через window.scrollTo
window.scrollTo({
top: 1000,
behavior: 'smooth'
});
// Способ 3: Прокрутить к конкретному элементу
const header = document.querySelector('header');
header.scrollIntoView({ behavior: 'smooth' });
IntersectionObserver (современный подход)
Это более эффективный способ для отловки видимости элементов:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('Элемент видим:', entry.target);
entry.target.classList.add('visible');
// Загрузить изображение лениво
if (entry.target.tagName === 'IMG') {
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target);
}
}
});
}, {
threshold: 0.1 // срабатывает когда 10% элемента видимо
});
// Наблюдать за всеми элементами с классом lazy
document.querySelectorAll('.lazy-image').forEach(img => {
observer.observe(img);
});
Практический пример: Header, реагирующий на скролл
const header = document.querySelector('header');
let lastScrollTop = 0;
window.addEventListener('scroll', () => {
const currentScroll = window.scrollY;
if (currentScroll > lastScrollTop) {
// Скролл вниз — спрятать header
header.classList.add('hidden');
} else {
// Скролл вверх — показать header
header.classList.remove('hidden');
}
lastScrollTop = currentScroll;
});
React хук для обработки скролла
import { useEffect } from 'react';
export function useScroll(callback) {
useEffect(() => {
const handleScroll = () => {
callback(window.scrollY);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [callback]);
}
// Использование
function MyComponent() {
useScroll((scrollY) => {
console.log('Текущий скролл:', scrollY);
});
return <div>Контент</div>;
}
Сравнение подходов
| Способ | Производительность | Браузер | Сложность |
|---|---|---|---|
| scroll событие | Хорошо (с throttle) | Все | Низкая |
| IntersectionObserver | Отличная | Современные | Средняя |
| requestAnimationFrame | Отличная | Все | Средняя |
| Scroll библиотеки | Переменная | Все | Высокая |