Реалізація 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

Реалізація кнопки «Наверх» на сайті

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

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 години. З прогресс-кільцем та доступністю — пів дня.