Плавная прокрутка при нажатии на ссылку привязки


488

У меня есть пара гиперссылок на моей странице. Часто задаваемые вопросы, которые пользователи прочтут, посетив мой раздел справки.

Используя ссылки привязки, я могу сделать прокрутку страницы в направлении привязки и направлять туда пользователей.

Есть ли способ сделать эту прокрутку плавной?

Но обратите внимание, что он использует собственную библиотеку JavaScript. Может быть, jQuery предлагает что-то подобное в выпечке?


Можете ли вы рассмотреть лучший ответ, может быть? Простое однострочное решение css трудно найти среди всех громоздких предложений jquery: stackoverflow.com/a/51588820/1422553
Александр Киричек

Ответы:


1160

Обновление от апреля 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, нарушая функцию back. Есть ли способ обойти это?
Флетч

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);
});

Упрощение : СУХОЙ

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*=\\#:

  • *означает, что это соответствует тому, что содержит #символ. Таким образом, только соответствуют якорям . Подробнее о значении этого см. Здесь
  • \\потому что #это специальный символ в 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^=\\#]
QuinnFreedman

3
Это привело к тому, что ссылки на якоря на других страницах перестали работать. Решается добавлением условного if ($ ($ (this.hash) .selector) .length) {... smooth scroll. }
Liren

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>

В связи с этим ссылка будет кликабельной и перейдет в любой раздел #sese, в данном случае это наш div, который мы назвали section.

Кстати, я часами пытался заставить это работать. Нашел решение в каком-то непонятном разделе комментариев. Он был глючным и не работал в некоторых тегах. Не работал в теле. Наконец-то это сработало, когда я поместил его в html {} в файле CSS.


4
Я могу быть очень удобным, но они недостатки
Buzut

3
хорошо, но будьте осторожны, потому что на данный момент он не поддерживается Safari и, очевидно, Explorer (03/2019)
Марко Романо

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 ();" может заменить "вернуть ложь";
Андрес Сепар

Извините, но не работает и отображается на странице с именем anchor быстро без какой-либо плавности.
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
Мне нравится этот ответ больше всего. Однако на самом деле нет никакого способа обеспечить компенсацию. Как будет необходимо в случае фиксированного заголовка.
bskool

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

15

Только CSS

html {
    scroll-behavior: smooth !important;
}

Все, что вам нужно добавить только это. Теперь ваша прокрутка внутренних ссылок будет плавной, как поток.

Примечание . Все последние браузеры ( Opera, Chromeи Firefoxт. Д.) Поддерживают эту функцию.

для подробного понимания прочитайте эту статью


1
отлично! Почему это не принятый ответ? нам не нужен весь этот JavaScript!
Тревор де

1
Это прекрасно работает, это должен быть принятый ответ.
гробница

Проверьте поддержку браузера здесь
Райан Чжоу

1
это работает как шарм. нет необходимости в
JS

Это лучшее решение для плавной прокрутки когда-либо! Спасибо!
еханный

10

Я предлагаю вам сделать этот общий код:

$('a[href^="#"]').click(function(){

var the_id = $(this).attr("href");

    $('html, body').animate({
        scrollTop:$(the_id).offset().top
    }, 'slow');

return false;});

Вы можете увидеть очень хорошую статью здесь: jquery-effet-smooth-scroll-defilement-fluide


9
Это не общее, это jQuery.
AnrDaemon

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
Кажется, это работает только для ссылок на внутренние страницы, но ссылки на другие страницы не работают, например, website.com/about-us/#who-we-are
rainerbrunotte

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-поведения. Добавьте следующее свойство.

    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: пожалуйста, проверьте совместимость браузера.


в какой контейнер следует использовать scroll-поведение: smooth;
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

Я сделал это для якорей "/ 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/, если ваши навигационные ссылки настроены в div навигации и объявлены с этой структурой:

<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

Если у вас есть простая кнопка на странице для прокрутки вниз до элемента div и вы хотите, чтобы кнопка «назад» работала, переходя наверх, просто добавьте этот код:

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

Это может быть расширено для перехода к различным элементам div, читая значение хеша и прокручивая, как ответ Джозефа Силберса.


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. Если вы этого не сделаете, функция position () всегда выдает разные значения позиции.


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.