Płynne przewijanie po kliknięciu linku kontrolnego

487

Mam kilka hiperłączy na mojej stronie. Często zadawane pytania, które użytkownicy przeczytają, gdy odwiedzą moją sekcję pomocy.

Za pomocą łączy zakotwiczenia mogę przewinąć stronę w kierunku zakotwiczenia i poprowadzić tam użytkowników.

Czy istnieje sposób na płynne przewijanie?

Ale zauważ, że używa niestandardowej biblioteki JavaScript. Może jQuery oferuje coś takiego upieczonego?

Tylko boliwijskie tutaj
źródło
Czy możesz przejrzeć najlepszą odpowiedź?
Jednowierszowe

Odpowiedzi:

1158

Aktualizacja kwietnia 2018 r .: Istnieje teraz natywny sposób :

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

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

Jest to obecnie obsługiwane tylko w najnowocześniejszych przeglądarkach.


W przypadku starszych przeglądarek możesz użyć tej techniki jQuery:

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

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

A oto skrzypce: http://jsfiddle.net/9SDLw/


Jeśli element docelowy nie ma identyfikatora i łączysz się z nim za pomocą name, użyj tego:

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

    return false;
});

Aby zwiększyć wydajność, należy buforować ten $('html, body')selektor, aby nie działał za każdym razem po kliknięciu kotwicy:

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

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

    return false;
});

Jeśli chcesz zaktualizować adres URL, zrób to w ramach animatewywołania zwrotnego:

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;
});
Joseph Silber
źródło
10
Wydaje się, że usuwa to #rozszerzenie z adresu URL, przerywając funkcję powrotu. Czy jest na to jakiś sposób?
Fletch
2
@JosephSilber że nie powinno być scrollTop: $(this.hash).offset().topzamiast scrollTop: $(this.href).offset().top?
Gregory Pakosz
4
@CreateSean -scrollTop: $(href).offset().top - 72
Joseph Silber,
5
Twierdziłbym, że buforowanie html, bodyobiektu tutaj nie jest konieczne, uruchamianie selektora raz na kliknięcie nie jest aż tak duże.
2
Pierwsze rozwiązanie jest najlepsze i najnowocześniejsze, możesz użyć tego wypełnienia, aby wesprzeć to zachowanie w starych przeglądarkach dzięki temu wypełnieniu
Efe
166

Prawidłowa składnia to:

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

Uproszczenie : OSUSZANIE

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

Wyjaśnienie href*=\\#:

  • *oznacza, że ​​pasuje do tego, co zawiera #char. Dlatego pasują tylko do kotwic . Więcej informacji na temat tego znaczenia znajduje się tutaj
  • \\to dlatego, że #jest to specjalny znak w selektorze css, więc musimy przed tym uciec.
Andres Separ
źródło
8
Musiałem zmienić, $('a')aby $('a[href*=#]')podawać tylko adresy URL kotwicy
okliv
2
@okliv To będzie za dużo służyć, na przykład link javascript <a href="javascript:$('#test').css('background-color', '#000')">Test</a>. Powinieneś raczej użyć, $('a[href^=#]')aby dopasować wszystkie adresy URL zaczynające się od znaku skrótu.
Martin Braun
3
również „#” jest znakiem specjalnym i należy go a[href^=\\#]
unikać w
3
Spowodowało to, że linki do kotwic na innych stronach przestały działać. Rozwiązany przez dodanie warunkowego płynnego przewijania if ($ ($ (this.hash) .selector) .length) {... }
Liren
1
Jak mogę to animować podczas pierwszej podróży na nową stronę? Na przykład klikając: website.com/newpage/#section2. Chciałbym, aby załadował stronę, a następnie przewinął w dół. Czy to jest możliwe?
Samyer
72

Nowa popularność w CSS3. Jest to o wiele łatwiejsze niż każda metoda wymieniona na tej stronie i nie wymaga Javascript. Wystarczy wpisać poniższy kod w css, a wszystkie nagłe linki prowadzą do lokalizacji na twojej stronie będą miały płynną animację przewijania.

html{scroll-behavior:smooth}

Następnie wszelkie linki wskazujące na div będą płynnie przesuwać się do tych sekcji.

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

Edycja: dla tych, którzy są zdezorientowani powyższym tagiem. Zasadniczo jest to link, który można kliknąć. Możesz mieć inny tag div gdzieś na swojej stronie internetowej, np

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

W związku z tym link będzie klikalny i przejdzie do dowolnego #section, w tym przypadku jest to nasz div, który nazwaliśmy sekcją.

BTW, spędziłem godziny próbując sprawić, żeby to zadziałało. Znalazłem rozwiązanie w jakiejś niejasnej sekcji komentarzy. To było błędne i nie działało w niektórych tagach. Nie działał w ciele. W końcu zadziałało, gdy wstawiłem go do html {} w pliku CSS.

Cristian Reyes
źródło
4
Mogę być bardzo przydatny, ale mają one wady
Buzut
3
fajnie, ale bądź ostrożny, ponieważ w tej chwili nie jest obsługiwany przez Safari i oczywiście przez Explorera (03/2019)
Marco Romano
2
fajne rozwiązanie, tylko zasięg jest ograniczony do 74,8%. może w przyszłości
iepur1lla
1
To niesamowite. Wielkie dzięki.
Mikkel Fennefoss,
1
To będzie najbardziej odpowiednia odpowiedź w nadchodzących latach.
Nurul Huda
22
$('a[href*=#]').click(function(event){
    $('html, body').animate({
        scrollTop: $( $.attr(this, 'href') ).offset().top
    }, 500);
    event.preventDefault();
});

to działało idealnie dla mnie

Philipp Sander
źródło
1
„event.preventDefault ();” może zastąpić „return false;”
Andres Separ
Przykro mi to mówić, ale nie działa i nie wyświetla się szybko na stronie o nazwie anchor bez żadnej płynności.
Kamlesh
18

Dziwi mnie, że nikt nie opublikował natywnego rozwiązania, które zajmuje się także aktualizacją skrótu lokalizacji przeglądarki, aby pasował. Oto on:

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

Zobacz samouczek: http://www.javascriptkit.com/javatutors/scrolling-html-bookmark-javascript.shtml

W przypadku witryn z lepkimi nagłówkami scroll-padding-topmożna użyć CSS w celu zapewnienia przesunięcia.

ptysie kokosowe
źródło
1
Najbardziej podoba mi się ta odpowiedź. Jednak afais nie ma możliwości zapewnienia offsetu. Jak by to było potrzebne w przypadku stałego nagłówka.
bskool
Niestety, to samo słabe wsparcie dla właściwości przewijania CSS: developer.mozilla.org/en-US/docs/Web/CSS/…
Dmitry Nevzorov
15

Tylko CSS

html {
    scroll-behavior: smooth !important;
}

Wystarczy tylko dodać to. Teraz Twoje przewijanie wewnętrznych linków będzie płynne jak przepływ strumienia.

Uwaga : Wszystkie najnowsze przeglądarki ( Opera, Chrome, Firefoxetc) obsługuje tę funkcję.

w celu szczegółowego zrozumienia przeczytaj ten artykuł

WasiF
źródło
1
miły! Dlaczego nie jest to akceptowana odpowiedź? nie potrzebujemy całego tego javascript!
Trevor de Koekkoek,
1
Działa świetnie, to powinna być zaakceptowana odpowiedź.
grób
Sprawdź obsługę przeglądarki tutaj
Ryan Zhou,
1
działa jak urok. nie potrzeba js
Navbro
To najlepsze rozwiązanie do płynnego przewijania NIGDY! Dzięki!
yehanny
10

Proponuję zrobić ten ogólny kod:

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

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

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

return false;});

Możesz zobaczyć bardzo dobry artykuł tutaj: jquery-effet-smooth-scroll-defilement-fluid

Sarah Bouyer
źródło
9
To nie jest ogólne, to jest 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;
      }
    }
  });
});

Oficjalny: http://css-tricks.com/snippets/jquery/smooth-scrolling/

KingRider
źródło
1
wydaje się, że działa to tylko w przypadku wewnętrznych linków zakotwiczenia strony, ale linki kotwiące z innych stron nie działają, na przykład website.com/about-us/#who-we-are
rainerbrunotte
5

Jest tu już wiele dobrych odpowiedzi - jednak wszyscy pomijają fakt, że należy wykluczyć puste kotwice . W przeciwnym razie skrypty generują błędy JavaScript, jak tylko kliknięta zostanie pusta kotwica.

Moim zdaniem poprawna odpowiedź jest następująca:

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

    $('html, body').animate({
        scrollTop: $($.attr(this, 'href')).offset().top
    }, 500);
});
Blackbam
źródło
4

Za pomocą JQuery:

$('a[href*=#]').click(function(){
  $('html, body').animate({
    scrollTop: $( $.attr(this, 'href') ).offset().top
  }, 500);
  return false;
});
brequinn
źródło
3

Podana odpowiedź działa, ale wyłącza linki wychodzące. Poniżej wersji z dodatkowym bonusem rozluźnienie (huśtawka) i szanuje linki wychodzące.

$(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;
        });
    });
});
Stóg
źródło
+1 za to, stop()że okruch adresu URL nie działa zgodnie z oczekiwaniami: przycisk Wstecz nie przywraca, dzieje się tak, ponieważ gdy okruchy są ustawione w adresie URL po zakończeniu animacji. Lepiej jest bez okruchów w adresie URL, na przykład tak robi airbnb.
Eric,
3

HTML

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

lub z bezwzględnym pełnym adresem 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;
            }
        }
    });
});
Jasmeet Singh
źródło
3

Nowoczesne przeglądarki są obecnie nieco szybsze. SetInterval może działać. Ta funkcja działa teraz dobrze w Chrome i Firefox. (Trochę powolny w safari, nie przeszkadzało to w 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
    }
}
Littleboots
źródło
3

Istnieje sposób CSS na wykonanie tego przy użyciu zachowania przewijania. Dodaj następującą właściwość.

    scroll-behavior: smooth;

I to jest to. Nie wymaga 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: sprawdź kompatybilność przeglądarki.

Santosh
źródło
do którego kontenera należy użyć przewijania: gładkie;
CraZyDroiD
W razie wątpliwości dodaj go do tagu body @CraZyDroiD
Santosh
2

Dodanie tego:

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

w jakiś sposób niweluje przesunięcie pionowe

top - 72

w Firefox i IE, ale nie w Chrome. Zasadniczo strona płynnie przewija się do punktu, w którym powinna się zatrzymać na podstawie przesunięcia, ale następnie przeskakuje w dół do miejsca, w którym strona poszedłaby bez przesunięcia.

Dodaje skrót na końcu adresu URL, ale naciśnięcie go wstecz nie zabierze Cię z powrotem na górę, po prostu usuwa skrót z adresu URL i pozostawia okno wyświetlania w miejscu, w którym się znajduje.

Oto pełny js, którego używam:

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;
});
Reid
źródło
2

To rozwiązanie będzie działać również w przypadku następujących adresów URL, bez przerywania linków zakotwiczonych do różnych stron.

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

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

itp.

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

Nie przetestowałem tego jeszcze we wszystkich przeglądarkach.

Midas
źródło
2

Ułatwi to, że jQuery rozpoznaje twój docelowy skrót i wie, kiedy i gdzie zatrzymać.

$('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;
    });
});
PanicBus
źródło
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;

    }



});
Abk
źródło
2

Testowany i zweryfikowany kod

<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>
Atif Tariq
źródło
1

Zrobiłem to zarówno dla kotwic href „/ xxxxx # asdf”, jak i „#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;
      }
    }
});
AndreDurao
źródło
1

Oto rozwiązanie, które wdrożyłem dla wielu linków i kotwic, dla płynnego przewijania:

http://www.adriantomic.se/development/jquery-localscroll-tutorial/ jeśli masz linki nawigacyjne skonfigurowane w div nawigacji i zadeklarowane za pomocą tej struktury:

<a href = "#destinationA">

oraz odpowiednie miejsca docelowe tagów zakotwiczenia:

<a id = "destinationA">

Następnie po prostu załaduj to do nagłówka dokumentu:

    <!-- 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>

Dzięki @Adriantomic

collyg
źródło
1

Jeśli masz prosty przycisk na stronie, aby przewinąć w dół do div i chcesz , aby przycisk Wstecz działał, przeskakując na górę, po prostu dodaj ten kod:

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

Można to rozszerzyć, aby przejść również do różnych div, czytając wartość skrótu i ​​przewijając jak Joseph Silbers odpowiedzi.

Niclas
źródło
1

Nigdy nie zapominaj, że funkcja offset () podaje pozycję elementu do udokumentowania. Więc kiedy potrzebujesz przewinąć swój element względem jego rodzica, powinieneś go użyć;

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

Kluczowym punktem jest pobranie scrollTop z scroll-div i dodanie go do scrollTop. Jeśli tego nie zrobisz, funkcja position () zawsze daje różne wartości pozycji.

rotring05
źródło
1

Można korzystać window.scroll()z behavior: smoothi topustawiony na przesunięcie szczytu, który zapewnia, że znacznik kotwica będzie na górze rzutni w tagu zakotwiczenia za.

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

Próbny:

Możesz po prostu ustawić właściwość CSS scroll-behaviorna smooth(którą obsługuje większość współczesnych przeglądarek), co eliminuje potrzebę Javascript.

hev1
źródło
0

dzięki za udostępnienie, Joseph Silber. Oto twoje rozwiązanie 2018 jako ES6 z niewielką zmianą, aby zachować standardowe zachowanie (przewiń do góry):

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"
    });
  });
});
Motine
źródło
0

Wymaga jquery i animacji do zakotwiczenia tagu o określonej nazwie zamiast id, przy jednoczesnym dodawaniu skrótu do adresu URL przeglądarki. Naprawiono również błąd w większości odpowiedzi z jquery, w których znak # nie jest poprzedzony odwrotnym ukośnikiem. Niestety przycisk Wstecz nie nawiguje prawidłowo do poprzednich łączy mieszających ...

$('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);
});
jjxtra
źródło