Реализация Back-to-Top кнопки на сайте

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация Back-to-Top кнопки на сайте
Простая
~2-3 часа
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Реализация Back-to-Top кнопки на сайте

Кнопка «Наверх» — минимальный компонент с одной задачей: плавно прокрутить страницу вверх и появляться только когда нужна. Детали реализации влияют на производительность и доступность.

CSS + минимальный JS

<button
  class="back-to-top"
  id="backToTop"
  aria-label="Прокрутить вверх"
  title="Наверх"
  hidden
>
  <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
    <path d="M12 4l-8 8h5v8h6v-8h5z" fill="currentColor"/>
  </svg>
</button>
.back-to-top {
  position: fixed;
  bottom: 32px;
  right: 32px;
  z-index: 50;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: #6366f1;
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 16px rgba(99, 102, 241, 0.4);
  transition: opacity 0.3s, transform 0.3s, background 0.2s;
  /* Предотвращаем Layout Shift — кнопка выведена из потока */
}

/* hidden атрибут добавляет display:none — переопределяем для анимации */
.back-to-top[hidden] {
  display: flex !important;
  opacity: 0;
  pointer-events: none;
  transform: translateY(8px);
}

.back-to-top:not([hidden]) {
  opacity: 1;
  transform: translateY(0);
}

.back-to-top:hover {
  background: #4f46e5;
  transform: translateY(-2px);
}

.back-to-top:active {
  transform: translateY(0);
}

/* Мобильные: не перекрываем нижнюю навигацию */
@media (max-width: 768px) {
  .back-to-top {
    bottom: calc(72px + env(safe-area-inset-bottom));
    right: 16px;
    width: 40px;
    height: 40px;
  }
}
const btn = document.getElementById('backToTop') as HTMLButtonElement

// Показываем после 400px скролла
const SHOW_THRESHOLD = 400
let ticking = false

window.addEventListener('scroll', () => {
  if (ticking) return
  ticking = true
  requestAnimationFrame(() => {
    btn.hidden = window.scrollY < SHOW_THRESHOLD
    ticking = false
  })
}, { passive: true })

btn.addEventListener('click', () => {
  window.scrollTo({ top: 0, behavior: 'smooth' })
  // Возвращаем фокус на первый фокусируемый элемент страницы
  const firstFocusable = document.querySelector<HTMLElement>(
    'a[href], button:not([disabled]), [tabindex="0"]'
  )
  firstFocusable?.focus({ preventScroll: true })
})

React-компонент

import { useEffect, useState } from 'react'

export function BackToTop({ threshold = 400 }: { threshold?: number }) {
  const [visible, setVisible] = useState(false)

  useEffect(() => {
    let ticking = false

    const handler = () => {
      if (ticking) return
      ticking = true
      requestAnimationFrame(() => {
        setVisible(window.scrollY > threshold)
        ticking = false
      })
    }

    window.addEventListener('scroll', handler, { passive: true })
    return () => window.removeEventListener('scroll', handler)
  }, [threshold])

  function scrollToTop() {
    window.scrollTo({ top: 0, behavior: 'smooth' })
  }

  return (
    <button
      onClick={scrollToTop}
      className={`back-to-top ${visible ? 'back-to-top--visible' : ''}`}
      aria-label="Прокрутить вверх"
      aria-hidden={!visible}
      tabIndex={visible ? 0 : -1}  // недоступна для Tab когда скрыта
    >
      <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
        <path d="M12 4l-8 8h5v8h6v-8h5z" fill="currentColor"/>
      </svg>
    </button>
  )
}

Вариант с прогрессом прокрутки

Популярная вариация — кружок вокруг кнопки, показывающий процент прочтения:

function BackToTopWithProgress({ threshold = 400 }: { threshold?: number }) {
  const [visible, setVisible] = useState(false)
  const [progress, setProgress] = useState(0)

  useEffect(() => {
    const handler = () => {
      const scrollY = window.scrollY
      const maxScroll = document.documentElement.scrollHeight - window.innerHeight
      setProgress(maxScroll > 0 ? (scrollY / maxScroll) * 100 : 0)
      setVisible(scrollY > threshold)
    }

    window.addEventListener('scroll', handler, { passive: true })
    return () => window.removeEventListener('scroll', handler)
  }, [threshold])

  const circumference = 2 * Math.PI * 18  // r=18
  const dashOffset = circumference - (progress / 100) * circumference

  return (
    <button
      onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
      className={`back-to-top-progress ${visible ? 'visible' : ''}`}
      aria-label={`Прокрутить вверх. Прочитано ${Math.round(progress)}%`}
      tabIndex={visible ? 0 : -1}
    >
      <svg viewBox="0 0 44 44" width="44" height="44">
        {/* Фоновый круг */}
        <circle cx="22" cy="22" r="18" fill="none" stroke="#e2e8f0" strokeWidth="3" />
        {/* Прогресс */}
        <circle
          cx="22" cy="22" r="18"
          fill="none"
          stroke="#6366f1"
          strokeWidth="3"
          strokeDasharray={circumference}
          strokeDashoffset={dashOffset}
          strokeLinecap="round"
          transform="rotate(-90 22 22)"
        />
        {/* Стрелка вверх */}
        <path d="M22 14l-6 6h4v8h4v-8h4z" fill="#6366f1" />
      </svg>
    </button>
  )
}

Плавность скролла: поведение в браузерах

scroll-behavior: smooth в CSS делает кнопку ещё проще:

html {
  scroll-behavior: smooth;
}

/* Но отключаем для пользователей с prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}
// Учитываем prefers-reduced-motion в JS
function scrollToTop() {
  const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches
  window.scrollTo({
    top: 0,
    behavior: prefersReduced ? 'instant' : 'smooth',
  })
}

Сроки

Кнопка с появлением/скрытием и плавным скроллом — 1–2 часа. С progress-кольцом и accessibility — полдня.