Avatar ·

Прокрутка страницы вверх с помощью JavaScript: простое и надёжное решение

Всем привет! Я новичок в JavaScript и никак не могу разобраться с прокруткой страницы наверх. На моём сайте есть длинная страница с контентом, и я хочу сделать кнопку «Наверх», чтобы пользователи могли быстро вернуться к началу. Пробовал использовать `window.scrollTo(0, 0)` — вроде работает, но резко, без анимации. Хочется плавной прокрутки. Пытался сделать через `window.scrollBy()`, но запутался в логике. Ещё пробовал старый метод `window.scrollTo({ top: 0, behavior: 'smooth' })`, но в некоторых браузерах он не срабатывает, особенно в старых версиях Safari. Вот мой текущий код, который я накидал:
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' }).