Skip to content

Всё для WordPress, веб-разработки — и не только

🔧 jQuery — плавный скролл к якорю при переходе с другой страницы

🔧 jQuery — плавный скролл к якорю при переходе с другой страницы

Вы добавили на страницу якорь, прописали #section в URL, и ждёте красивого плавного перехода. Пользователь кликает с другой страницы, браузер честно подставляет хеш... и дёргается рывком в нужное место. Никакого скролла. Просто мгновенный прыжок.

Проблема в том, что браузер по умолчанию обрабатывает хеш мгновенно, до того, как DOM полностью готов и скрипты успели перехватить событие. А когда пользователь приходит с внешней ссылки, ваш click-обработчик вообще не срабатывает, события клика не было, был прямой переход по URL.

Мы собрали два проверенных jQuery-решения: базовый скрипт для простых случаев и продвинутый вариант с обработкой кликов, загрузки страницы и обходом конфликтов. Оба протестированы на реальных проектах и работают с актуальными версиями jQuery.

💡 Быстрый обзор:

  • Сохранить хеш из URL во временную переменную
  • Очистить location.hash, чтобы браузер не прыгнул рывком
  • Проскроллить к якорю через animate() с нужным отступом
  • Восстановить хеш для правильного URL в адресной строке

Зачем вообще плавный скролл к якорю

Когда посетитель переходит по ссылке site.com/page/#pricing, он ожидает увидеть блок с ценами, а не верх страницы. Резкий прыжок дезориентирует, пользователь не понимает, где оказался и что было до этого. Плавная прокрутка даёт контекст: глаза успевают зацепиться за промежуточные секции, мозг строит маршрут «откуда → куда».

С нативным CSS-свойством scroll-behavior: smooth история простая, добавили в таблицу стилей, и браузер сам скроллит к якорю. Но этот подход ломается ровно в тот момент, когда вам нужен кастомный отступ под фиксированную шапку. 110 пикселей отступа, и блок не уезжает под header, а остаётся ровно там, где его ждёт пользователь. jQuery-решение даёт этот контроль.

Плюс, кроссбраузерность. scroll-behavior: smooth не работает в Internet Explorer (да, такие проекты ещё живы), а jQuery-метод animate() отрабатывает везде одинаково.

Базовый скрипт: минимум кода, максимум пользы

Первый вариант, компактный, на 10 строк. Он решает ровно одну задачу: плавно проскроллить к якорю, когда пользователь пришёл с другой страницы или по прямой ссылке с хешем.

1jQuery(document).ready(function ($) {
2 var myHash = location.hash; // сохраняем хеш
3 location.hash = ''; // очищаем — браузер перестаёт дёргаться
4 if (myHash[1] != undefined) { // хеш не пустой?
5 $('html, body').animate(
6 { scrollTop: $(myHash).offset().top - 110 },
7 700 // длительность в миллисекундах
8 );
9 location.hash = myHash; // возвращаем хеш обратно
10 }
11});

Разбор по шагам, потому что три строчки творят магию, но важно понимать механику:

  • var myHash = location.hash, читаем хеш из адресной строки (например, #pricing). На этом этапе браузер ещё ничего не скроллит.
  • location.hash = '', ключевой трюк. Очищаем хеш, и браузер теряет цель для мгновенного прыжка. Без этой строки скрипт будет «бороться» со встроенным поведением браузера, и проиграет.
  • $(myHash).offset().top - 110, вычисляем позицию якоря от верха документа и вычитаем 110 пикселей. Это ваш отступ под фиксированную шапку. Если шапка position: fixed высотой 80px, ставьте 80 (или 90 с запасом). Если шапки нет, уберите вычитание совсем.
  • $('html, body').animate(...), плавно скроллим за 700 миллисекунд. Селектор 'html, body', не прихоть: разные браузеры скроллят либо html, либо body, поэтому пишем оба для надёжности.
  • location.hash = myHash, возвращаем хеш. Теперь адресная строка снова показывает #pricing, а страница уже красиво припаркована у нужного блока.

Скрипт вставляется в functions.php темы, в кастомный JS-файл или через плагин вроде Code Snippets. Главное, он должен быть в блоке jQuery(document).ready().

Продвинутый вариант: обработка кликов, загрузка страницы и защита от конфликтов

Базовый скрипт покрывает большинство сценариев, но бывают ситуации, когда location.hash = '' не срабатывает: браузер успевает прыгнуть до очистки хеша. Тогда подключаем второй вариант, он тяжелее, но надёжнее.

1jQuery(document).ready(function () {
2 jQuery(window).bind('load', function () {
3 jQuery('a:not(.spu-clickable)[href*="#"]:not([href="#"])').click(function () {
4 if (
5 location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') ||
6 location.hostname == this.hostname
7 ) {
8 var target = jQuery(this.hash);
9 target = target.length ? target : jQuery('[name=' + this.hash.slice(1) + ']');
10 if (target.length) {
11 jQuery('html, body').animate(
12 { scrollTop: target.offset().top - 37 },
13 1000
14 );
15 return false;
16 }
17 }
18 });
19 });
20});
21
22jQuery(window).load(function () {
23 function goToByScroll(id) {
24 jQuery('html, body').animate(
25 { scrollTop: jQuery('#' + id).offset().top - 38 },
26 1000
27 );
28 }
29 if (window.location.hash != '') {
30 goToByScroll(window.location.hash.substr(1));
31 }
32});

Что здесь происходит, по блокам:

Блок первый, обработчик клика по внутренним ссылкам с якорем. jQuery(window).bind('load', ...) гарантирует, что все элементы страницы (включая картинки и iframe) загружены до привязки обработчика. Фильтр a:not(.spu-clickable) исключает ссылки, которые уже обрабатывает попап-плагин (SPU, WordPress PopUp), чтобы анимации не конфликтовали. Добавьте свои классы-исключения по аналогии.

Блок второй, обработка перехода с внешней страницы. jQuery(window).load() срабатывает позже document.ready и гарантирует, что все элементы, включая якорь, уже в DOM. Функция goToByScroll(id) делает ровно то же, что и базовый скрипт, но вызывается только после полной загрузки окна. substr(1) отрезает символ # от хеша.

Важный нюанс: jQuery(window).load() считается устаревшим начиная с jQuery 3.0. Если ваш проект на актуальной версии jQuery, замените на:

1jQuery(window).on('load', function () {
2 // код здесь
3});

Разница в синтаксисе, поведение идентичное.

Тонкая настройка: отступы, селекторы и подводные камни

Скрипт рабочий, но каждая вёрстка индивидуальна. Три частых доработки, которые спасали на реальных проектах.

Проблема первая, offset().top возвращает не те координаты. Такое бывает, когда у родительских блоков задано CSS-свойство transform, filter или will-change, они создают новый контекст позиционирования, и offset() считает относительно него, а не документа. Решение, вариант с offset({top: -0}):

1jQuery('html, body').animate(
2 { scrollTop: jQuery('#' + id).offset({ top: -0 }).top },
3 1000
4);

Параметр {top: -0} заставляет jQuery пересчитать позицию от границ документа, игнорируя промежуточные контексты. Выглядит как магия, но работает.

Проблема вторая, селектор 'html, body' скроллит не тот элемент. В некоторых темах скролл-контейнером выступает не html или body, а конкретный блок, например, body.home для главной страницы или .main-content для внутренних. Определите свой контейнер через DevTools (вкладка Elements → поиск по overflow: scroll или overflow: auto) и подставьте в селектор:

1jQuery('body.home').animate(
2 { scrollTop: target.offset().top - 37 },
3 1000
4);

Проблема третья, хеш содержит кириллицу или спецсимволы. location.hash выдаст URL-кодированную последовательность вместо читаемой строки, и jQuery-селектор $(myHash) не найдёт элемент. Решение, декодировать хеш через decodeURIComponent() перед подстановкой в селектор.

Видео: живой пример скролла с внешним переходом

Короткое демо от John Smith, плавный скролл к якорю через jQuery animate() на реальной странице с фиксированной шапкой и внешним переходом.

⁉️🤔 Частые вопросы

Скрипт работает внутри страницы, но не работает при переходе с другого URL. Почему?

Потому что click-обработчик не срабатывает, клика не было. Пользователь перешёл по прямой ссылке, и браузер сам обработал хеш до выполнения вашего JS. Лечится связкой location.hash = '' + animate() + location.hash = myHash, именно в такой последовательности. Очистка хеша отменяет встроенный прыжок, анимация скроллит плавно, возврат хеша сохраняет правильный URL в адресной строке.

Какой отступ ставить под фиксированную шапку?

Точную высоту шапки плюс несколько пикселей воздуха. Откройте DevTools (F12), выделите элемент header вашего сайта, посмотрите его height во вкладке Computed. Например, шапка 80px, ставьте 90 в коде. Если на мобильной версии шапка другой высоты, добавьте медиа-запрос с динамическим расчётом отступа.

jQuery всё ещё актуален в 2026?

Да, особенно в экосистеме WordPress. jQuery остаётся в ядре WP, множество плагинов и тем зависят от него, и миграция всей экосистемы на нативный JS займёт годы. Для новых проектов на чистом JavaScript плавный скролл делается через element.scrollIntoView({ behavior: 'smooth' }) или scroll-behavior: smooth в CSS, строк кода значительно меньше. Но если вы правите существующий WP-сайт, jQuery-решение по-прежнему надёжное и уместное.

Можно ли обойтись без jQuery?

Можно и нужно, если проект новый. Нативный аналог базового скрипта:

1document.addEventListener('DOMContentLoaded', () => {
2 const hash = window.location.hash;
3 if (hash) {
4 window.location.hash = '';
5 const target = document.querySelector(hash);
6 if (target) {
7 window.scrollTo({
8 top: target.getBoundingClientRect().top + window.pageYOffset - 110,
9 behavior: 'smooth'
10 });
11 }
12 window.location.hash = hash;
13 }
14});

Тот же принцип, сохранить хеш, очистить, проскроллить, вернуть. Разница в том, что behavior: 'smooth' использует нативный CSS-движок браузера, а не jQuery-таймер. Скорость не настраивается, зато код легче на 30 КБ (вес jQuery).

Почему $('html, body'), а не просто $('html')?

Firefox скроллит html, Chrome использует body, Safari зависит от версии. Указание обоих селекторов в animate() гарантирует работу на всех браузерах: jQuery отправит анимацию туда, где она реально применится.

Стоит ли городить jQuery ради плавного скролла в 2026

Если у вас WordPress-сайт или legacy-проект, где jQuery уже подключён, берите скрипт из статьи и не усложняйте. Десять строк кода, которые работают везде, с любым браузером и не требуют полифилов. Для новой вёрстки на чистом JavaScript, используйте нативный scroll-behavior: smooth или scrollIntoView(). Оба варианта дают плавный скролл, но jQuery-подход оставляет вам контроль над скоростью, отступом и селектором скролл-контейнера, в нативной реализации это потребует отдельных проверок.

Главное правило, которое мы вынесли из десятка проектов: всегда очищайте хеш до скролла. Без этого трюка ни один обработчик не победит встроенное поведение браузера, и пользователь так и будет прыгать рывком.