Кнопка «Наверх» на CSS и JavaScript без jQuery
← Блог
Frontend · 03.10.2026 · 5 мин чтения

Кнопка «Наверх» на CSS и JavaScript без jQuery

Кнопка «Наверх» возвращает читателя в начало длинной страницы. Сделаем её на HTML, CSS и JavaScript: ссылка отвечает за переход, CSS — за плавную прокрутку, а IntersectionObserver — за появление после первого экрана.

Пример работает без jQuery. Кнопка доступна с клавиатуры и учитывает системную настройку уменьшения анимации. В конце показано, как подключить её в WordPress.

1. HTML-разметка кнопки «Наверх»

Добавь перед закрывающим </body> ссылку и невидимый маркер первого экрана:

<div class="to-top-sentinel" aria-hidden="true"></div>

<a class="to-top" href="#top" data-hidden>
  <svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
    <path d="M12 5l-7 7 1.4 1.4 4.6-4.6V19h2V8.8l4.6 4.6L19 12z" fill="currentColor"/>
  </svg>
  <span class="visually-hidden">Наверх</span>
</a>

Ссылка href="#top" работает без обработчика клика. Если элемента с id="top" нет, браузер перейдёт в начало документа. Если такой элемент есть, ссылка приведёт к нему.

После перехода проверь навигацию клавишей Tab: обход должен продолжиться с начала страницы. В адресной строке появится #top; вариант без якоря разобран ниже.

Иконка скрыта от скринридеров, а доступное имя даёт текст «Наверх». Если в теме уже есть элемент с id="top", например шапка, ссылка приведёт к нему — это тоже подходящий вариант.

2. CSS: положение кнопки и плавная прокрутка

.to-top {
  position: fixed;
  right: 16px;
  bottom: max(16px, env(safe-area-inset-bottom));
  z-index: 100;
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: #1f2937;
  color: #fff;
  transition: opacity 0.2s, transform 0.2s, visibility 0.2s;
}

.to-top[data-hidden] {
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
}

.to-top:focus-visible {
  outline: 3px solid #f59e0b;
  outline-offset: 3px;
}

@media (hover: hover) {
  .to-top:hover {
    background: #374151;
  }
}

.to-top-sentinel {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 100vh;
  pointer-events: none;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

@media (prefers-reduced-motion: reduce) {
  .to-top {
    transition: none;
  }
}

Что здесь важно:

  • Размер 48 × 48 пикселей удобен для пальца.
  • visibility: hidden убирает спрятанную кнопку из порядка фокуса. Одной opacity: 0 для этого мало: на невидимую ссылку можно было бы перейти клавиатурой.
  • env(safe-area-inset-bottom) не даёт кнопке залезть под системную полосу на iPhone. Чтобы значение было ненулевым, в <meta name="viewport"> должен быть viewport-fit=cover; без него сработает обычный отступ 16 пикселей.
  • Подсветка при наведении включена только там, где есть настоящее наведение. Подробнее — в статье про :hover и :active на сенсорных устройствах.
  • Плавная прокрутка отключается для тех, кто включил в системе уменьшение анимации.

scroll-behavior: smooth на html влияет на все якорные ссылки сайта. Если фиксированная шапка закрывает заголовки при переходе по якорям, добавь scroll-margin-top, как описано в статье о якорных ссылках.

3. JavaScript: показать кнопку после первого экрана

const toTop = document.querySelector('.to-top');
const sentinel = document.querySelector('.to-top-sentinel');

if (toTop && sentinel && 'IntersectionObserver' in window) {
  const observer = new IntersectionObserver(([entry]) => {
    toTop.toggleAttribute('data-hidden', entry.isIntersecting);
  });

  observer.observe(sentinel);
}

Маркер занимает первый экран страницы. Пока он виден, у кнопки есть атрибут data-hidden. Когда маркер уходит вверх за край окна, атрибут снимается и кнопка появляется. Браузер сообщает только о смене состояния, поэтому код не выполняется на каждом пикселе прокрутки, как обработчик scroll.

Атрибут data-hidden стоит в разметке изначально, поэтому кнопка не мелькает при загрузке. Если JavaScript не выполнится, кнопка останется скрытой, а страница продолжит работать как обычно.

Прокрутка наверх без #top в адресе

Когда якорь в адресе недопустим, например в SPA с роутингом по hash, используй <button type="button"> и прокрути страницу из скрипта. В этом случае фокус нужно перенести вручную, иначе пользователь клавиатуры останется внизу страницы:

button.addEventListener('click', () => {
  const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  window.scrollTo({ top: 0, behavior: reduce ? 'auto' : 'smooth' });

  // Первый фокусируемый элемент страницы, например ссылка на главную в шапке
  document.querySelector('header a')?.focus({ preventScroll: true });
});

Подключение в WordPress

В классической теме вставь разметку в footer.php перед wp_footer(). Стили добавь в основной CSS темы, а скрипт сохрани в assets/js/to-top.js и подключи в functions.php:

function mytheme_enqueue_to_top() {
    wp_enqueue_script(
        'mytheme-to-top',
        get_theme_file_uri( 'assets/js/to-top.js' ),
        array(),
        wp_get_theme()->get( 'Version' ),
        array(
            'strategy'  => 'defer',
            'in_footer' => true,
        )
    );
}
add_action( 'wp_enqueue_scripts', 'mytheme_enqueue_to_top' );

Параметр strategy поддерживается с WordPress 6.3. Четвёртый аргумент задаёт версию файла для кеша браузера. В примере это версия темы: увеличивай её после изменения скрипта.

Проверка кнопки на компьютере и телефоне

  • Кнопка не видна на первом экране и появляется после прокрутки.
  • На телефоне она не перекрывает баннер cookie, чат или другие фиксированные элементы.
  • С клавиатуры кнопка получает видимый фокус, а после нажатия Tab продолжает обход с начала страницы.
  • При включённом уменьшении анимации страница перескакивает наверх без плавной прокрутки.

Документация

Есть вопрос или дополнение?

Обсудить в Telegram

Добавить комментарий