
🖱 Плавне прокручування до якоря: CSS, JavaScript і jQuery — три робочих підходи
Користувач клацає за посиланням з якорем, і сторінка миттєво перестрибує в потрібне місце. Жодної анімації, жодної плавності, жодного розуміння, де ти опинився. Рваний перехід збиває з пантелику, особливо на довгих лендингах і в документації.
Додати плавний скрол до якоря можна за 5 хвилин. Але диявол у деталях: посилання може вести із зовнішнього сайту, в URL уже є хеш, а зверху висить фіксований хедер, який перекриває якір. Звичайний scroll-behavior: smooth цих нюансів не вирішує.
Нижче три робочі підходи: чистий CSS, нативний JavaScript і jQuery. Кожен із кодом, який можна забрати й вставити в проєкт. Плюс окремо розбираємо зовнішні переходи, щоб скрол спрацьовував, навіть коли користувач приходить з іншої сторінки або з пошукової видачі.
💡 Швидкий огляд:
- Вмикаємо плавний скрол одним CSS-рядком, без JavaScript взагалі
- Додаємо відступ під фіксований хедер через
scroll-margin-top - Пишемо нативний JS з
scrollIntoView, для зовнішніх переходів і тонкого налаштування - Розбираємо jQuery-варіант з
animate(), коли потрібна кастомна швидкість і контроль - Обробляємо зовнішній хеш: сторінка завантажилася, якір в URL, і плавно їдемо до цілі
Навіщо взагалі плавний скрол
Різкий стрибок до якоря дезорієнтує. Користувач втрачає контекст: де я був, куди потрапив, що пропустив? Плавне прокручування зберігає просторову орієнтацію, оку є за що зачепитися, поки сторінка рухається.
З погляду сприйняття сайт із плавним скролом виглядає відполірованим. Особливо це помітно на довгих сторінках: лендинги, документація, FAQ-розділи, зміст статей. Користувач клацає «Тарифи» в меню, і сторінка не смикається, а м’яко сповзає до блоку з цінами.
Технічно задача розпадається на дві: скрол під час кліку за внутрішнім посиланням (користувач уже на сторінці) і скрол під час зовнішнього переходу (в URL одразу є #anchor). У другому випадку браузер за замовчуванням стрибає до якоря миттєво ще до повного завантаження, і це треба перехоплювати.
CSS scroll-behavior, один рядок замість скрипту
Найпростіший і найсучасніший спосіб. Браузери навчилися плавному скролу нативно, достатньо одного CSS-правила:
1 html { 2 scroll-behavior: smooth; 3 }
Усе. Будь-який перехід за якірним посиланням стає плавним. Жодного JavaScript, жодних бібліотек.
Підтримка: усі сучасні браузери з 2022 року: Chrome 61+, Firefox 36+, Safari 15.4+, Edge 79+. Єдиний виняток, IE11, але його частка трафіку прямує до нуля.
Фіксований хедер: scroll-margin-top
Якщо над контентом висить фіксована шапка (sticky header), якір після скролу опиниться під нею. Вирішується одним рядком:
1 h2, h3, [id] { 2 scroll-margin-top: 80px; /* высота хедера + отступ */ 3 }
Браузер сам додасть відступ під час скролу до цільового елемента. Раніше для цього писали милиці з padding-top і від’ємним margin, тепер не потрібно.
Обмеження чистого CSS
У CSS-підходу немає контролю над швидкістю анімації та функцією плавності (easing). Браузер використовує вбудовану криву, зазвичай ease-in-out. Якщо потрібна кастомна швидкість або нелінійна анімація, переходимо до JavaScript.
Нативний JavaScript: scrollIntoView
Коли одного CSS недостатньо, на допомогу приходить метод scrollIntoView() з опцією behavior: 'smooth':
1 document.querySelectorAll('a[href^="#"]').forEach(anchor => { 2 anchor.addEventListener('click', function(e) { 3 e.preventDefault(); 4 const target = document.querySelector(this.getAttribute('href')); 5 if (target) { 6 target.scrollIntoView({ 7 behavior: 'smooth', 8 block: 'start' 9 }); 10 } 11 }); 12 });
Цей код перехоплює клік за всіма посиланнями з #, скасовує стандартний перехід і запускає плавний скрол. Працює без jQuery і без додаткових бібліотек.
Плюс підходу: можна програмно керувати: змінювати швидкість через CSS scroll-behavior на <html>, додавати умови (наприклад, виключати певні посилання), комбінувати з scroll-margin-top для хедера.
Мінус: scrollIntoView не дає змоги задати швидкість анімації безпосередньо. Для кастомного часу скролу (скажімо, 800 мс замість браузерних ~500 мс) потрібен або jQuery, або ручний requestAnimationFrame.
JQuery animate, повний контроль над анімацією
Якщо проєкт уже використовує jQuery або потрібне тонке налаштування (швидкість, відступ під хедер, фільтрація посилань), ось актуальний скрипт. Нижче адаптована версія класичного підходу, переписана під сучасний jQuery:
1 jQuery(document).ready(function($) { 2 // Плавный скролл при клике на якорную ссылку 3 $('a[href*="#"]:not([href="#"])').on('click', function() { 4 if ( 5 location.pathname.replace(/^\//, '') === this.pathname.replace(/^\//, '') 6 && location.hostname === this.hostname 7 ) { 8 var target = $(this.hash); 9 target = target.length ? target : $('[name="' + this.hash.slice(1) + '"]'); 10 if (target.length) { 11 $('html, body').animate({ 12 scrollTop: target.offset().top - 80 13 }, 800); 14 return false; 15 } 16 } 17 }); 18 19 // Скролл при внешнем переходе с хешем в URL 20 if (window.location.hash) { 21 var hash = window.location.hash.substring(1); 22 var $target = $('#' + hash); 23 if ($target.length) { 24 setTimeout(function() { 25 $('html, body').animate({ 26 scrollTop: $target.offset().top - 80 27 }, 800); 28 }, 100); 29 } 30 } 31 });
Що тут змінилося відносно старих версій, які кочують блогами:
.bind()замінено на.on(),.bind()оголошено застарілим з jQuery 3.0 і буде видалено в jQuery 4.0.- Селектор
'a:not(.spu-clickable)[href*="#"]:not([href="#"])'спрощено, специфічний клас.spu-clickableстосувався конкретного плагіна (Popups by OptinMonster) і в загальному випадку не потрібен. jQuery(window).bind("load", ...)прибрано, друга частина скрипту для зовнішнього хешу тепер живе в$(document).ready()і не потребує окремої подіїload.- Додано затримку
setTimeout(..., 100), щоб DOM гарантовано відрендерився до розрахунку позиції якоря (актуально для сторінок із лінивим завантаженням зображень і динамічним контентом).
Куди вставляти код
Три варіанти на вибір:
- Через файл теми. Додайте скрипт у
functions.phpчерезwp_enqueue_script(), стандартний спосіб для дочірньої теми. - Плагін Code Snippets. Вставте код як новий сніпет з автозапуском, не загубиться під час оновлення теми.
- Кастомний JS-плагін. Якщо на сайті вже використовується плагін для вставки довільного JavaScript (наприклад, WPCode), додайте код туди.
Обробка зовнішнього переходу з хешем, детальний розбір
Найхитріша частина задачі. Користувач переходить за посиланням https://site.com/page/#pricing з іншого сайту або з пошуку. Браузер за замовчуванням миттєво стрибає до #pricing, щойно елемент з’являється в DOM, без анімації і часто до повного завантаження сторінки.
jQuery-скрипт вище вирішує це двома кроками:
- Визначає наявність хешу через
window.location.hash. Якщо хеш є, читає його значення (без#). - Шукає ціль і скролить.
$('#' + hash)знаходить елемент,setTimeoutдає сторінці відрендеритися,animate()веде до цілі з відступом 80 пікселів.
Затримка в 100 мілісекунд, емпірична. На швидких сторінках вистачає і 50 мс, на важких (лендинги з фоновим відео, картами, графіками) краще збільшити до 200-300 мс. Альтернатива, дочекатися події window.load замість document.ready, але тоді скрол відбудеться тільки після завантаження всіх зображень, що може затягнутися.
Що робити, якщо якір не знаходить ціль
Іноді хеш в URL вказує на неіснуючий елемент. Наприклад, користувач помилився або сторінку змінили. У цьому випадку ні CSS, ні JS-підхід не впадуть із помилкою, просто нічого не відбудеться. Браузер проігнорує неіснуючий якір. Плавний скрол теж мовчки відпрацює (умова if (target.length) не пустить далі).
Порівняння трьох підходів
Критерій | CSS scroll-behavior | JS scrollIntoView | jQuery animate |
|---|---|---|---|
Складність впровадження | 1 рядок CSS | 10-15 рядків JS | 25-30 рядків JS + jQuery |
Контроль швидкості | Немає | Немає (браузерний) | Так (мс) |
Зовнішній хеш | Так, нативно | Потребує дод. коду | Потребує дод. коду |
Відступ під хедер |
|
| Ручний в |
Залежності | Немає | Немає | jQuery 3.x |
Підтримка браузерами | 96%+ | 96%+ | 99%+ (з jQuery) |
Для більшості сучасних проєктів оптимальний CSS-підхід: один рядок, нуль залежностей, нативна продуктивність. Якщо потрібен відступ під хедер, додається scroll-margin-top. Якщо потрібна кастомна швидкість або підтримка давніх браузерів, jQuery з animate().
Нижче відео з живою демонстрацією всіх трьох підходів, від CSS до jQuery:
⁉️🤔 Часті запитання
Чому плавний скрол не працює на мобільних пристроях?
В iOS Safari до версії 15.4
scroll-behavior: smoothне підтримувався. Зараз підтримка є (Safari 15.4+, березень 2022). Якщо скрол не працює на старих iPhone, додайте JavaScript-фолбек зscrollIntoViewабоrequestAnimationFrame. Також перевірте, чи не блокує скролoverflow: hiddenнаbody, деякі мобільні меню ставлять його під час відкриття. Станом на червень 2026 року глобальна підтримкаscroll-behavior, 96,3% браузерів (дані Can I Use).
Як зробити плавний скрол не до якоря, а до довільного елемента за кліком на кнопку?
Дайте елементу
idі використовуйте той самий JavaScript, але прив’язаний до кнопки, а не до якірного посилання. Наприклад, кнопка «Повернутися нагору»:document.querySelector('#back-to-top').addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' })). Для jQuery-варіанта:$('#back-to-top').on('click', () => $('html, body').animate({ scrollTop: 0 }, 600)). Жодних якорів не потрібно, скролимося до координати.
Чи потрібно підключати jQuery лише заради плавного скролу в 2026 році?
Ні. jQuery тягне близько 87 КБ у min-версії (30 КБ gzip). Якщо на сайті більше ніде не використовується jQuery, підключати його заради одного скролу нераціонально. Беріть CSS-підхід з
scroll-behavior: smoothабо нативнийscrollIntoView. jQuery виправданий, тільки якщо сайт уже зібраний на ньому (теми на jQuery, legacy-проєкти) і ви хочете додати скрол у наявну кодову базу.
Чи можна анімувати скрол із різною швидкістю: спочатку швидко, потім повільно?
Так, через
$.animate()з jQuery UI або черезrequestAnimationFrameз функцією плавності (easing). Стандартний jQueryanimate()використовує лінійну або swing-функцію. Для нестандартних кривих підключайтеjquery.easingабо пишіть свій цикл наrequestAnimationFrameз кастомною функцією плавності, наприклад, easeInOutQuad. Це дає повний контроль над швидкістю, траєкторією і не потребує зовнішніх бібліотек.
Як перевірити, що скрол працює під час зовнішнього переходу з хешем?
Відкрийте нову вкладку, вставте URL з якорем (наприклад,
https://вашсайт.ру/page/#contacts) і натисніть Enter. Сторінка має завантажитися і плавно проскролити до блоку. Другий варіант: у консолі браузера на відкритій сторінці виконатиlocation.hash = '#contacts'. Якщо скрол плавний, скрипт працює. Якщо сторінка смикнулася миттєво, обробник зовнішнього хешу не налаштовано.
Який підхід обрати під вашу задачу
Якщо сайт сучасний, без jQuery, **CSS **scroll-behavior: smooth закриває переважну більшість сценаріїв. Один рядок, миттєвий результат. Додайте scroll-margin-top під хедер, і готово.
Якщо сайт на WordPress з jQuery у темі, **jQuery-скрипт з **animate(). Дає контроль над швидкістю і відступом, обробляє зовнішні переходи, не конфліктує з іншими плагінами.
Якщо пишете з нуля і не хочете тягнути бібліотеку, **нативний **scrollIntoView() з обробником зовнішнього хешу. Золота середина: чисто, швидко, без залежностей.
Беріть код зі статті, вставляйте в проєкт і перевіряйте зовнішній перехід — це єдиний кейс, який легко пропустити під час тестування.



