Плавне прокручування при натисканні на якірне посилання


487

На своїй сторінці у мене є кілька гіперпосилань. Поширені запитання, які користувачі прочитають, коли вони відвідують мій довідковий розділ.

Використовуючи посилання на якір, я можу змусити прокручувати сторінку до якоря та направляти користувачів туди.

Чи є спосіб зробити цю прокрутку гладкою?

Але зауважте, що він використовує власну бібліотеку JavaScript. Можливо, jQuery пропонує щось подібне, запечене в цьому?


Чи можете ви, будь ласка, переглянути найкращу відповідь? Чисте рішення CSS одна лінія важко знайти серед всіх громіздких пропозицій Jquery: stackoverflow.com/a/51588820/1422553
Олександр Киричок

Відповіді:


1159

Оновлення квітня 2018 року: тепер це є рідний спосіб зробити це :

document.querySelectorAll('a[href^="#"]').forEach(anchor => {
    anchor.addEventListener('click', function (e) {
        e.preventDefault();

        document.querySelector(this.getAttribute('href')).scrollIntoView({
            behavior: 'smooth'
        });
    });
});

Наразі це підтримується лише у найбільш кровоточивих кращих браузерах.


Для старшої підтримки браузера ви можете використовувати цю техніку jQuery:

$(document).on('click', 'a[href^="#"]', function (event) {
    event.preventDefault();

    $('html, body').animate({
        scrollTop: $($.attr(this, 'href')).offset().top
    }, 500);
});

А ось загадка: http://jsfiddle.net/9SDLw/


Якщо ваш цільовий елемент не має ідентифікатора, а ви посилаєтесь на нього за його допомогою name, скористайтеся цим:

$('a[href^="#"]').click(function () {
    $('html, body').animate({
        scrollTop: $('[name="' + $.attr(this, 'href').substr(1) + '"]').offset().top
    }, 500);

    return false;
});

Для підвищення продуктивності слід кешувати цей $('html, body')селектор, щоб він не запускався щоразу, коли клацнув якір:

var $root = $('html, body');

$('a[href^="#"]').click(function () {
    $root.animate({
        scrollTop: $( $.attr(this, 'href') ).offset().top
    }, 500);

    return false;
});

Якщо ви хочете оновити URL-адресу, зробіть це в межах animateзворотного дзвінка:

var $root = $('html, body');

$('a[href^="#"]').click(function() {
    var href = $.attr(this, 'href');

    $root.animate({
        scrollTop: $(href).offset().top
    }, 500, function () {
        window.location.hash = href;
    });

    return false;
});

10
Це, здається, видаляє #extension з URL-адреси, порушуючи функцію задньої частини. Чи є шлях до цього?
Флетч

2
@JosephSilber чи не повинен це бути scrollTop: $(this.hash).offset().topзамість scrollTop: $(this.href).offset().top?
Григорій Пакош

4
@CreateSean -scrollTop: $(href).offset().top - 72
Джозеф

5
Я б стверджував, що кешування html, bodyоб'єкта тут непотрібне, запуск селектора один раз за клік насправді не так вже й багато.

2
Перше рішення є найкращим та найсучаснішим, ви можете використовувати цей поліфайл для підтримки такої поведінки у старих браузерах за допомогою цього поліфілу
Efe

166

Правильний синтаксис:

//Smooth scrolling with links
$('a[href*=\\#]').on('click', function(event){     
    event.preventDefault();
    $('html,body').animate({scrollTop:$(this.hash).offset().top}, 500);
});

// Smooth scrolling when the document is loaded and ready
$(document).ready(function(){
  $('html,body').animate({scrollTop:$(location.hash).offset().‌​top}, 500);
});

Спрощення : DRY

function smoothScrollingTo(target){
  $('html,body').animate({scrollTop:$(target).offset().​top}, 500);
}
$('a[href*=\\#]').on('click', function(event){     
    event.preventDefault();
    smoothScrollingTo(this.hash);
});
$(document).ready(function(){
  smoothScrollingTo(location.hash);
});

Пояснення href*=\\#:

  • *означає, що він відповідає тому, що містить #char. Таким чином, відповідають лише якорям . Більш детально про значення цього курсу дивіться тут
  • \\це тому, що #у css-селекторі є особлива особливість, тому нам доведеться уникати цього.

8
я повинен був змінити, $('a')щоб $('a[href*=#]')обслуговувати лише URL-адреси якоря
okliv

2
@okliv Це буде слугувати занадто багато, наприклад, посиланням на javascript <a href="javascript:$('#test').css('background-color', '#000')">Test</a>. Ви повинні скористатися, $('a[href^=#]')щоб відповідати всім URL-адресам, які починаються з хеш-символу.
Мартін Браун

3
також "#" - це особливий символ, і його потрібно a[href^=\\#]
уникати так

3
Це призвело до припинення роботи посилань на якіри на інших сторінках. Вирішується додаванням умовного if ($ ($ (this.hash) .selector) .length) {... гладка прокрутка. }
Лірен

1
Як я можу це оживити під час першого переходу на нову сторінку? Наприклад, натиснувши: website.com/newpage/#section2. Я хотів би, щоб він завантажив сторінку, а потім прокрутив вниз. Це можливо?
Самєр

72

Нова гарячість у CSS3. Це набагато простіше, ніж кожен метод, перелічений на цій сторінці, і не вимагає Javascript. Просто введіть код нижче у свій css, і раптом посилання вкажуть на місця, розташовані на вашій власній сторінці, матиме плавну анімацію прокрутки.

html{scroll-behavior:smooth}

Після цього будь-які посилання, спрямовані на div, плавно перейдуть до цих розділів.

<a href="#section">Section1</a>

Редагувати: для тих, хто плутає вищевказаний тег. В основному це посилання, на яке можна натискати. Потім ви можете мати ще один тег div, наприклад, десь на своїй веб-сторінці

<div classname="section">content</div>

У зв'язку з цим посилання буде доступним для натискання і перейде до # секції, у цьому випадку це наш розділ, який ми назвали розділом.

До речі, я витратив години на те, щоб змусити це працювати. Рішення знайшов у розділі незрозумілих коментарів. Він був баггі і не працював у деяких тегах. Не працювали в організмі. Нарешті він спрацював, коли я помістив його у html {} у файл CSS.


4
Я можу бути дуже корисним, але вони є недоліками
Buzut

3
приємно, але будьте обережні, адже на даний момент його не підтримує Safari, а очевидно, Explorer (03/2019)
Marco Romano

2
приємне рішення, лише покриття обмежено 74,8%. можливо, у майбутньому
iepur1lla

1
Це дивовижне. Дуже дякую.
Міккель Феннефосс

1
Це буде найбільш релевантною відповіддю в наступні роки.
Нурул Худа

22
$('a[href*=#]').click(function(event){
    $('html, body').animate({
        scrollTop: $( $.attr(this, 'href') ).offset().top
    }, 500);
    event.preventDefault();
});

це працювало ідеально для мене


1
"event.preventDefault ();" може замінити "return false;"
Андрес Сепар

Вибачте, але не працює і показується на сторінці з назвою якір швидко, без плавності.
Kamlesh

18

Я здивований, що ніхто не опублікував власне рішення, яке також піклується про оновлення хешу розташування веб-переглядача. Ось:

let anchorlinks = document.querySelectorAll('a[href^="#"]')
 
for (let item of anchorlinks) { // relitere 
    item.addEventListener('click', (e)=> {
        let hashval = item.getAttribute('href')
        let target = document.querySelector(hashval)
        target.scrollIntoView({
            behavior: 'smooth',
            block: 'start'
        })
        history.pushState(null, null, hashval)
        e.preventDefault()
    })
}

Дивіться підручник: http://www.javascriptkit.com/javatutors/scrolling-html-bookmark-javascript.shtml

Для сайтів із липкими заголовками scroll-padding-topCSS можна використовувати для надання зрушення.


1
Мені подобається ця відповідь найбільше. Однак у afais немає способу забезпечити компенсацію. Як це було б потрібно у випадку фіксованого заголовка.
bskool

На жаль, така ж погана підтримка, як і для властивості CSS scroll-поведінки: developer.mozilla.org/en-US/docs/Web/CSS/…
Дмитро Невзоров

15

Тільки CSS

html {
    scroll-behavior: smooth !important;
}

Все, що вам потрібно лише додати. Тепер поведінка прокрутки внутрішніх посилань буде гладкою, як потік потоку.

Примітка : Всі останні браузери ( Opera, Chrome, і Firefoxт.д.) підтримує цю функцію.

для детального розуміння читайте цю статтю


1
приємно! Чому це не прийнята відповідь? нам не потрібен весь цей javascript!
Trevor de Koekkoek

1
Це чудово працює, це повинна бути прийнята відповідь.
гробниця

Ознайомтеся з підтримкою браузера тут
Ryan Zhou

1
це працює як шарм. не потрібно js
Navbro

Це найкраще рішення для плавного прокручування ВСЕ! Дякую!
yehanny


6
$(function() {
  $('a[href*=#]:not([href=#])').click(function() {
    if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
      var target = $(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
      if (target.length) {
        $('html,body').animate({
          scrollTop: target.offset().top
        }, 1000);
        return false;
      }
    }
  });
});

Офіційно: http://css-tricks.com/snippets/jquery/smooth-scrolling/


1
це, здається, працює лише на внутрішні посилання на якір, але прив’язки інших сторінок не працюють, наприклад, веб
сайт.com/about-us/#who-we-are

5

Тут вже є багато хороших відповідей - проте всі вони пропускають той факт, що порожні якіри повинні бути виключені . Інакше ці сценарії генерують помилки JavaScript, як тільки натискається порожній якір.

На мою думку, правильна відповідь така:

$('a[href*=\\#]:not([href$=\\#])').click(function() {
    event.preventDefault();

    $('html, body').animate({
        scrollTop: $($.attr(this, 'href')).offset().top
    }, 500);
});

4

Використання JQuery:

$('a[href*=#]').click(function(){
  $('html, body').animate({
    scrollTop: $( $.attr(this, 'href') ).offset().top
  }, 500);
  return false;
});


3

Надана відповідь працює, але вимикає вихідні посилання. Нижче версії з додатковим бонусом полегшується (розгойдується) та поважає вихідні посилання.

$(document).ready(function () {
    $('a[href^="#"]').on('click', function (e) {
        e.preventDefault();

        var target = this.hash;
        var $target = $(target);

        $('html, body').stop().animate({
            'scrollTop': $target.offset().top
        }, 900, 'swing', function () {
            window.location.hash = target;
        });
    });
});

+1, stop()однак крихта URL не працює, як очікувалося: кнопка "Назад" не повертається назад, це тому, що коли крихта встановлюється в URL після завершення анімації. Краще без крихти в URL-адресі, наприклад, так працює airbnb.
Ерік

3

HTML

<a href="#target" class="smooth-scroll">
    Link
</a>
<div id="target"></div>

або з абсолютною повною URL-адресою

<a href="https://somewebsite.com/#target" class="smooth-scroll">
    Link
</a>
<div id="target"></div>

jQuery

$j(function() {
    $j('a.smooth-scroll').click(function() {
        if (
                window.location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '')
            &&  window.location.hostname == this.hostname
        ) {
            var target = $j(this.hash);
            target = target.length ? target : $j('[name=' + this.hash.slice(1) + ']');
            if (target.length) {
                $j('html,body').animate({
                    scrollTop: target.offset().top - 70
                }, 1000);
                return false;
            }
        }
    });
});

3

Сучасні браузери в цей час трохи швидші. SetInterval може працювати. Ця функція добре працює в Chrome і Firefox в наші дні. (Трохи повільно в сафарі, не турбував IE)

function smoothScroll(event) {
    if (event.target.hash !== '') { //Check if tag is an anchor
        event.preventDefault()
        const hash = event.target.hash.replace("#", "")
        const link = document.getElementsByName(hash) 
        //Find the where you want to scroll
        const position = link[0].getBoundingClientRect().y 
        let top = 0

        let smooth = setInterval(() => {
            let leftover = position - top
            if (top === position) {
                clearInterval(smooth)
            }

            else if(position > top && leftover < 10) {
                top += leftover
                window.scrollTo(0, top)
            }

            else if(position > (top - 10)) {
                top += 10
                window.scrollTo(0, top)
            }

        }, 6)//6 milliseconds is the faster chrome runs setInterval
    }
}

3

Існує спосіб css зробити це за допомогою поведінки прокрутки. Додайте наступне властивість.

    scroll-behavior: smooth;

І це все. Не потрібно JS.

a {
  display: inline-block;
  width: 50px;
  text-decoration: none;
}
nav, scroll-container {
  display: block;
  margin: 0 auto;
  text-align: center;
}
nav {
  width: 339px;
  padding: 5px;
  border: 1px solid black;
}
scroll-container {
  display: block;
  width: 350px;
  height: 200px;
  overflow-y: scroll;
  scroll-behavior: smooth;
}
scroll-page {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  font-size: 5em;
}
<nav>
  <a href="#page-1">1</a>
  <a href="#page-2">2</a>
  <a href="#page-3">3</a>
</nav>
<scroll-container>
  <scroll-page id="page-1">1</scroll-page>
  <scroll-page id="page-2">2</scroll-page>
  <scroll-page id="page-3">3</scroll-page>
</scroll-container>

PS: перевірте сумісність браузера.


до якого контейнера я повинен використовувати поведінку прокрутки: гладкий;
CraZyDroiD

У разі сумнівів, додайте його до тегу body @CraZyDroiD
Santosh

2

Додавання цього:

function () {
    window.location.hash = href;
}

якимось чином зводить нанівець вертикальне зміщення

top - 72

в Firefox та IE, але не в Chrome. В основному сторінка плавно прокручується до тієї точки, в якій вона повинна зупинитися на основі зміщення, але потім стрибає вниз, куди сторінка піде без зміщення.

Він додає хеш до кінця URL-адреси, але натискання назад не поверне вас до вершини, воно просто видаляє хеш з URL-адреси та залишає вікно перегляду, де він сидить.

Ось повний js, який я використовую:

var $root = $('html, body');
$('a').click(function() {
    var href = $.attr(this, 'href');
    $root.animate({
        scrollTop: $(href).offset().top - 120
    }, 500, function () {
        window.location.hash = href;
    });
    return false;
});

2

Це рішення також буде працювати для наступних URL-адрес, не порушуючи прив’язних посилань на різні сторінки.

http://www.example.com/dir/index.html
http://www.example.com/dir/index.html#anchor

./index.html
./index.html#anchor

тощо.

var $root = $('html, body');
$('a').on('click', function(event){
    var hash = this.hash;
    // Is the anchor on the same page?
    if (hash && this.href.slice(0, -hash.length-1) == location.href.slice(0, -location.hash.length-1)) {
        $root.animate({
            scrollTop: $(hash).offset().top
        }, 'normal', function() {
            location.hash = hash;
        });
        return false;
    }
});

Я ще не перевіряв це у всіх браузерах.


2

Це дозволить легко дозволити jQuery розпізнати ваш цільовий хеш і знати, коли і де зупинитися.

$('a[href*="#"]').click(function(e) {
    e.preventDefault();
    var target = this.hash;
    $target = $(target);

    $('html, body').stop().animate({
        'scrollTop': $target.offset().top
    }, 900, 'swing', function () {
        window.location.hash = target;
    });
});

2
$("a").on("click", function(event){
    //check the value of this.hash
    if(this.hash !== ""){
        event.preventDefault();

        $("html, body").animate({scrollTop:$(this.hash).offset().top}, 500);

        //add hash to the current scroll position
        window.location.hash = this.hash;

    }



});

2

Випробуваний і перевірений код

<script>
jQuery(document).ready(function(){
// Add smooth scrolling to all links
jQuery("a").on('click', function(event) {

// Make sure this.hash has a value before overriding default behavior
if (this.hash !== "") {
  // Prevent default anchor click behavior
  event.preventDefault();

  // Store hash
  var hash = this.hash;

  // Using jQuery's animate() method to add smooth page scroll
  // The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area
  jQuery('html, body').animate({
    scrollTop: jQuery(hash).offset().top
  }, 800, function(){

    // Add hash (#) to URL when done scrolling (default click behavior)
    window.location.hash = hash;
  });
} // End if
});
});
</script>

1

Я зробив це для якорів href "/ xxxxx # asdf" та "#asdf"

$("a[href*=#]").on('click', function(event){
    var href = $(this).attr("href");
    if ( /(#.*)/.test(href) ){
      var hash = href.match(/(#.*)/)[0];
      var path = href.match(/([^#]*)/)[0];

      if (window.location.pathname == path || path.length == 0){
        event.preventDefault();
        $('html,body').animate({scrollTop:$(this.hash).offset().top}, 1000);
        window.location.hash = hash;
      }
    }
});

1

Ось рішення, яке я реалізував для декількох посилань і якорів, для плавного прокручування:

http://www.adriantomic.se/development/jquery-localscroll-tutorial/ якщо у вас є навігаційні посилання, встановлені в навігаційному розділі та оголошені за допомогою цієї структури:

<a href = "#destinationA">

і відповідні місця призначення тега якоря:

<a id = "destinationA">

Потім просто завантажте це в голову документа:

    <!-- Load jQuery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>

<!-- Load ScrollTo -->
<script src="http://flesler-plugins.googlecode.com/files/jquery.scrollTo-1.4.2-min.js"></script>

<!-- Load LocalScroll -->
<script src="http://flesler-plugins.googlecode.com/files/jquery.localscroll-1.2.7-min.js"></script>

<script type = "text/javascript">
 $(document).ready(function()
    {
        // Scroll the whole document
        $('#menuBox').localScroll({
           target:'#content'
        });
    });
</script>

Завдяки @Adriantomic


1

Якщо у вас на сторінці є проста кнопка, яку потрібно прокрутити вниз до діва та хочете, щоб кнопка "Назад" спрацювала, перейшовши вгору, просто додайте цей код:

$(window).on('hashchange', function(event) {
    if (event.target.location.hash=="") {
        window.scrollTo(0,0);
    }
});

Це також можна розширити, щоб перейти до різних дівок, читаючи хеш-значення та прокручуючи відповіді Джозефа Сілберса.


1

Ніколи не забувайте, що функція offset () надає позиції вашого елемента документувати. Отже, коли вам потрібно прокрутити ваш елемент відносно його батьківського, ви повинні використовувати це;

    $('.a-parent-div').find('a').click(function(event){
        event.preventDefault();
        $('.scroll-div').animate({
     scrollTop: $( $.attr(this, 'href') ).position().top + $('.scroll-div').scrollTop()
     }, 500);       
  });

Ключовим моментом є отримання scrollTop з scroll-div та додавання його до scrollTop. Якщо ви не зробите цю функцію, позиція () завжди дає різні значення позиції.


1

Ви можете використовувати window.scroll()з behavior: smoothі topвстановлені у верхній частина зсуву анкерного тега , який гарантує , що якір тег буде у верхній частині вікна перегляду.

document.querySelectorAll('a[href^="#"]').forEach(a => {
    a.addEventListener('click', function (e) {
        e.preventDefault();
        var href = this.getAttribute("href");
        var elem = document.querySelector(href)||document.querySelector("a[name="+href.substring(1, href.length)+"]");
        //gets Element with an id of the link's href 
        //or an anchor tag with a name attribute of the href of the link without the #
        window.scroll({
            top: elem.offsetTop, 
            left: 0, 
            behavior: 'smooth' 
        });
        //if you want to add the hash to window.location.hash
        //you will need to use setTimeout to prevent losing the smooth scrolling behavior
       //the following code will work for that purpose
       /*setTimeout(function(){
            window.location.hash = this.hash;
        }, 2000); */
    });
});

Демонстрація:

Ви можете просто встановити для властивості CSS scroll-behaviorзначення smooth(яке підтримує більшість сучасних браузерів), що уникне потреби в JavaScript.


0

дякую за обмін, Джозеф Сілбер. Ось ваше рішення 2018 як ES6 з незначною зміною, щоб зберегти стандартну поведінку (прокрутіть доверху):

document.querySelectorAll("a[href^=\"#\"]").forEach((anchor) => {
  anchor.addEventListener("click", function (ev) {
    ev.preventDefault();

    const targetElement = document.querySelector(this.getAttribute("href"));
    targetElement.scrollIntoView({
      block: "start",
      alignToTop: true,
      behavior: "smooth"
    });
  });
});

0

Потрібен jquery та анімація, щоб прив’язати тег до вказаного імені замість id, додавши хеш до URL-адреси браузера. Також виправляє помилку в більшості відповідей за допомогою jquery, де знак # не має префіксу за допомогою зворотної косої риски. Кнопка "Назад", на жаль, не належним чином переходить до попередніх хеш-посилань ...

$('a[href*=\\#]').click(function (event)
{
    let hashValue = $(this).attr('href');
    let name = hashValue.substring(1);
    let target = $('[name="' + name + '"]');
    $('html, body').animate({ scrollTop: target.offset().top }, 500);
    event.preventDefault();
    history.pushState(null, null, hashValue);
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.