Прокрутка страницы вверх с помощью JavaScript: простое и надёжное решение
function scrollToTop() {
window.scrollTo(0, 0);
}
Кнопка работает, но дёргано. Подскажите, как сделать плавную прокрутку, которая будет работать во всех современных браузерах? И можно ли добавить анимацию с изменением скорости — чтобы сначала быстро, потом замедлялась? Буду благодарен за пример с пояснениями. Спасибо!
P.S. jQuery не предлагайте — хочу на чистом JS.
<<Современное решение с плавной прокруткой
Вы на верном пути! Действительно, window.scrollTo(0, 0) работает мгновенно. Для плавной прокрутки используйте объект options со свойством behavior: 'smooth'. Это поддерживается во всех современных браузерах (Chrome, Firefox, Edge, Opera, Safari 15.4+).
Базовый пример
function scrollToTop() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
}
Этот код создаёт плавную анимацию прокрутки к верхнему краю страницы. Но он не поддерживается в старых версиях Safari (до 15.4). Для кросс-браузерности есть два пути:
- Проверка поддержки — используем
'scrollBehavior' in document.documentElement.style. - Полифилл — собственная реализация анимации.
Кросс-браузерное решение с проверкой
function scrollToTop() {
// Проверяем, поддерживает ли браузер smooth scroll
if ('scrollBehavior' in document.documentElement.style) {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
} else {
// Ручная анимация для старых браузеров
const duration = 600; // мс
const start = window.pageYOffset;
const startTime = performance.now();
function scroll(timestamp) {
const elapsed = timestamp - startTime;
const progress = Math.min(elapsed / duration, 1);
// Функция easing — замедление в конце
const eased = progress < 0.5
? 2 * progress * progress
: 1 - Math.pow(-2 * progress + 2, 2) / 2;
window.scrollTo(0, start * (1 - eased));
if (progress < 1) {
requestAnimationFrame(scroll);
}
}
requestAnimationFrame(scroll);
}
}
Объяснение кода
- Проверка поддержки —
'scrollBehavior' in document.documentElement.styleвозвращаетtrue, если браузер понимаетbehavior: 'smooth'. - Длительность —
duration = 600 мс(можно менять). - Easing-функция — используем квадратичное замедление (ease-in-out). Начинается быстро, замедляется к концу.
- requestAnimationFrame — синхронизирует анимацию с частотой обновления экрана (обычно 60 кадров/с).
Улучшенная версия с кастомным easing
Хотите другой эффект? Замените easing-функцию:
// Линейная
function linear(t) { return t; }
// Замедление в конце (ease-out)
function easeOut(t) { return 1 - Math.pow(1 - t, 3); }
// Ускорение в начале (ease-in)
function easeIn(t) { return t * t; }
// Пружинистый эффект
function bounce(t) {
const n1 = 7.5625;
const d1 = 2.75;
if (t < 1 / d1) return n1 * t * t;
else if (t < 2 / d1) return n1 * (t -= 1.5 / d1) * t + 0.75;
else if (t < 2.5 / d1) return n1 * (t -= 2.25 / d1) * t + 0.9375;
else return n1 * (t -= 2.625 / d1) * t + 0.984375;
}
Привязка к кнопке
<button onclick="scrollToTop()">Наверх</button>
Этот подход работает во всех браузерах: современных — через нативную smooth-прокрутку, в старых — через ручную анимацию. Вы можете регулировать скорость и стиль прокрутки, меняя параметры duration и easing-функцию.
Альтернативный подход с использованием Intersection Observer
Иногда нужно не просто прокрутить наверх, а показать/скрыть кнопку в зависимости от положения скролла. Используйте Intersection Observer:
const topObserver = new IntersectionObserver(
(entries) => {
const btn = document.getElementById('scrollTopBtn');
btn.style.display = entries[0].isIntersecting ? 'none' : 'block';
},
{ threshold: 0.1 }
);
// Наблюдаем за элементом в начале страницы
topObserver.observe(document.querySelector('#page-top'));
Кнопка будет появляться только когда пользователь прокрутит вниз. Сам механизм прокрутки можно оставить из первого ответа.
Минималистичный вариант для React/Vue
Если используете фреймворк, можно сделать через refs:
// React
const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
};
Для Vue:
methods: {
scrollToTop() {
window.scrollTo({ top: 0, behavior: 'smooth' });
}
}
Обратите внимание: в SPA-приложениях window.scrollTo работает корректно только если корневой элемент — body. Если у вас кастомный контейнер с overflow: scroll, вызывайте метод на нём: container.scrollTo({ top: 0, behavior: 'smooth' }).