Scorrimento uniforme quando si fa clic su un collegamento di ancoraggio


487

Ho un paio di collegamenti ipertestuali sulla mia pagina. Una FAQ che gli utenti leggeranno quando visitano la mia sezione di aiuto.

Usando i collegamenti di ancoraggio, posso far scorrere la pagina verso l'ancoraggio e guidare gli utenti lì.

C'è un modo per facilitare lo scorrimento?

Ma nota che sta usando una libreria JavaScript personalizzata. Forse jQuery offre qualcosa di simile al forno?


Puoi per favore rivedere la migliore risposta forse? Soluzione css una riga puro è difficile da trovare tra tutti i suggerimenti jQuery ingombranti: stackoverflow.com/a/51588820/1422553
Александр Киричек

Risposte:


1160

Aggiornamento aprile 2018: esiste ora un modo nativo per farlo :

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

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

Questo è attualmente supportato solo nei browser più all'avanguardia.


Per il supporto del browser precedente, è possibile utilizzare questa tecnica jQuery:

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

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

Ed ecco il violino: http://jsfiddle.net/9SDLw/


Se il tuo elemento target non ha un ID e ti stai collegando tramite esso name, usa questo:

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

    return false;
});

Per migliorare le prestazioni, è necessario memorizzare nella cache quel $('html, body')selettore, in modo che non venga eseguito ogni volta che si fa clic su un ancoraggio:

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

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

    return false;
});

Se vuoi che l'URL venga aggiornato, fallo nel animatecallback:

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
Questo sembra rimuovere l'estensione # dall'URL, interrompendo la funzione back. C'è un modo per aggirare questo?
Fletch,

2
@JosephSilber non dovrebbe essere scrollTop: $(this.hash).offset().topinvece di scrollTop: $(this.href).offset().top?
Gregory Pakosz,

4
@CreateSean -scrollTop: $(href).offset().top - 72
Joseph Silber,

5
Direi che la memorizzazione nella cache html, bodydell'oggetto qui non è necessaria, eseguire un selettore una volta per clic non è poi così tanto.

2
La prima soluzione è la migliore e la più moderna, puoi usare questo polyfill per supportare questo comportamento sui vecchi browser con questo polyfill
Efe,

166

La sintassi corretta è:

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

Semplificazione : SECCO

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

Spiegazione di href*=\\#:

  • *significa che corrisponde a ciò che contiene #char. Quindi abbina solo ancore . Per ulteriori informazioni sul significato di questo, vedere qui
  • \\è perché #è un carattere speciale nel selettore CSS, quindi dobbiamo evitarlo.

8
ho dovuto cambiare $('a')per $('a[href*=#]')servire solo gli URL di ancoraggio
okliv

2
@okliv Questo servirà troppo, ad esempio un collegamento javascript come <a href="javascript:$('#test').css('background-color', '#000')">Test</a>. Dovresti piuttosto utilizzare $('a[href^=#]')per abbinare tutti gli URL che iniziano con un carattere hash.
Martin Braun,

3
inoltre, '#' è un personaggio speciale e deve essere evaso in questo modo:a[href^=\\#]
QuinnFreedman,

3
Ciò ha causato l'interruzione dei collegamenti agli ancoraggi su altre pagine. Risolto aggiungendo un condizionale if ($ ($ (this.hash) .selector) .length) {... smooth scroll. }
Liren,

1
Come posso animarlo quando viaggio per la prima volta in una nuova pagina? Ad esempio, facendo clic su: website.com/newpage/#section2. Vorrei caricare la pagina e quindi scorrere verso il basso. È possibile?
Samyer,

72

Il nuovo hotness in CSS3. Questo è molto più semplice di ogni metodo elencato in questa pagina e non richiede Javascript. Basta inserire il codice qui sotto nel tuo CSS e tutti i collegamenti improvvisi puntano a posizioni all'interno della tua pagina avranno un'animazione scorrevole.

html{scroll-behavior:smooth}

Dopodiché, qualsiasi collegamento puntato verso un div scivolerà dolcemente verso quelle sezioni.

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

Modifica: per quelli confusi su quanto sopra un tag. Fondamentalmente è un link su cui è possibile fare clic. Puoi quindi avere un altro tag div da qualche parte nella tua pagina web come

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

A questo proposito il link a sarà cliccabile e andrà a qualunque #section sia, in questo caso è il nostro div che abbiamo chiamato sezione.

A proposito, ho passato ore a cercare di farlo funzionare. Ho trovato la soluzione in una sezione di commenti oscuri. Era difettoso e non funzionava con alcuni tag. Non ha funzionato nel corpo. Alla fine ha funzionato quando l'ho messo in HTML {} nel file CSS.


4
Posso essere molto utile ma sono degli svantaggi
Buzut

3
bello, ma fai attenzione perché al momento non è supportato da Safari e ovviamente da Explorer (03/2019)
Marco Romano

2
bella soluzione, solo la copertura è limitata al 74,8%. forse in futuro
iepur1lla,

1
È fantastico. Molte grazie.
Mikkel Fennefoss,

1
Questa sarà la risposta più rilevante nei prossimi anni.
Nurul Huda,

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

questo ha funzionato perfettamente per me


1
"event.preventDefault ();" può sostituire "return false;"
Andres Separ

Mi dispiace dirlo, ma non funziona e non viene visualizzato rapidamente sulla pagina denominata anchor senza alcuna scorrevolezza.
Kamlesh,

18

Sono sorpreso che nessuno abbia pubblicato una soluzione nativa che si occupi anche dell'aggiornamento dell'hash della posizione del browser in modo che corrisponda. Ecco qui:

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

Vedi tutorial: http://www.javascriptkit.com/javatutors/scrolling-html-bookmark-javascript.shtml

Per i siti con intestazioni adesive, è scroll-padding-toppossibile utilizzare CSS per fornire un offset.


1
Mi piace di più questa risposta. Tuttavia, non è possibile fornire un offset. Come sarebbe necessario nel caso di un'intestazione fissa.
bskool,

Sfortunatamente, lo stesso scarso supporto della proprietà del comportamento di scorrimento CSS: developer.mozilla.org/en-US/docs/Web/CSS/…
Dmitry Nevzorov

15

Solo CSS

html {
    scroll-behavior: smooth !important;
}

Tutto ciò che serve per aggiungere solo questo. Ora il tuo comportamento di scorrimento dei link interni sarà fluido come un flusso-flusso.

Nota : Tutti i browser più recenti ( Opera, Chrome, Firefoxecc) supporta questa funzione.

per la comprensione dei dettagli, leggi questo articolo


1
simpatico! Perché questa non è la risposta accettata? non abbiamo bisogno di tutto quel javascript!
Trevor de Koekkoek,

1
Funziona benissimo, questa dovrebbe essere la risposta accettata.
Tomba

Controlla qui il
Ryan Zhou,

1
funziona come un fascino. non c'è bisogno di js
Navbro

Questa è la soluzione migliore per uno scorrimento fluido MAI! Grazie!
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;
      }
    }
  });
});

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


1
questo sembra funzionare solo per i collegamenti di ancoraggio della pagina interna, ma i collegamenti di ancoraggio di altre pagine non funzionano, ad esempio website.com/about-us/#who-we-are
rainerbrunotte,

5

Ci sono già molte buone risposte qui - tuttavia mancano tutte del fatto che le ancore vuote debbano essere escluse . In caso contrario, tali script generano errori JavaScript non appena si fa clic su un'ancora vuota.

Secondo me la risposta corretta è questa:

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

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

4

Utilizzando JQuery:

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


3

La risposta fornita funziona ma disabilita i collegamenti in uscita. Di seguito una versione con un bonus aggiuntivo allevia (swing) e rispetta i link in uscita.

$(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 per il stop() tuttavia la briciola di url non funziona come previsto: il pulsante Indietro non riporta indietro, questo perché quando la briciola è impostata nell'URL dopo il completamento dell'animazione. È meglio senza una briciola nell'URL, ad esempio è così che fa airbnb.
Eric,

3

HTML

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

o Con URL completo assoluto

<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

I browser moderni sono un po 'più veloci in questi giorni. Un setInterval potrebbe funzionare. Questa funzione funziona bene in Chrome e Firefox in questi giorni. (Un po 'lento in Safari, non ha disturbato con 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

C'è un modo css per farlo usando il comportamento scroll. Aggiungi la seguente proprietà.

    scroll-behavior: smooth;

E questo è tutto. Nessun JS richiesto.

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: si prega di verificare la compatibilità del browser.


a quale contenitore dovrei usare il comportamento a scorrimento: liscio;
CraZyDroiD,

In caso di dubbio, aggiungilo al tag body @CraZyDroiD
Santosh

2

Aggiungendo questo:

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

sta in qualche modo annullando l'offset verticale

top - 72

in Firefox e IE, ma non in Chrome. Fondamentalmente, la pagina scorre senza intoppi fino al punto in cui dovrebbe fermarsi in base all'offset, ma poi passa al punto in cui la pagina andrebbe senza l'offset.

Aggiunge l'hash alla fine dell'URL, ma premere indietro non ti riporta in cima, rimuove semplicemente l'hash dall'URL e lascia la finestra di visualizzazione dove si trova.

Ecco i js completi che sto usando:

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

Questa soluzione funzionerà anche per i seguenti URL, senza interrompere i collegamenti di ancoraggio a pagine diverse.

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

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

eccetera.

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

Non l'ho ancora provato su tutti i browser.


2

Ciò renderà più semplice consentire a jQuery di discernere l'hash di destinazione e sapere quando e dove fermarsi.

$('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

Codice testato e verificato

<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

Ho fatto questo per entrambe le ancore "/ xxxxx # asdf" e "#asdf" href

$("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

Ecco la soluzione che ho implementato per più collegamenti e ancore, per uno scorrimento regolare:

http://www.adriantomic.se/development/jquery-localscroll-tutorial/ se hai i tuoi collegamenti di navigazione impostati in un div di navigazione e dichiarati con questa struttura:

<a href = "#destinationA">

e le destinazioni dei tag di ancoraggio corrispondenti in questo modo:

<a id = "destinationA">

Quindi basta caricare questo nella testa del documento:

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

Grazie a @Adriantomic


1

Se hai un semplice pulsante sulla pagina per scorrere fino a un div e vuoi che il pulsante Indietro funzioni saltando in alto, aggiungi questo codice:

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

Questo potrebbe essere esteso per passare anche a diversi div, leggendo il valore di hash e scorrendo come la risposta di Joseph Silbers.


1

Non dimenticare mai che la funzione offset () sta dando la posizione del tuo elemento al documento. Quindi quando hai bisogno di scorrere il tuo elemento rispetto al suo genitore dovresti usare questo;

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

Il punto chiave è ottenere scrollTop di scroll-div e aggiungerlo a scrollTop. Se non lo farai, la funzione position () ti darà sempre valori di posizione diversi.


1

È possibile utilizzare window.scroll()con behavior: smoothe topimpostare al top di offset del tag di ancoraggio che assicura che il tag di ancoraggio sarà in cima alla finestra.

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

demo:

Puoi semplicemente impostare la proprietà CSS scroll-behaviorsu smooth(supportata dalla maggior parte dei browser moderni) che elimina la necessità di Javascript.


0

grazie per la condivisione, Joseph Silber. Ecco la tua soluzione 2018 come ES6 con una piccola modifica per mantenere il comportamento standard (scorrere verso l'alto):

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

Richiede jquery e anima per ancorare il tag con il nome specificato anziché id, aggiungendo l'hash all'URL del browser. Risolve anche un errore nella maggior parte delle risposte con jquery in cui il segno # non è preceduto da una barra rovesciata di escape. Il pulsante Indietro, sfortunatamente, non torna correttamente ai precedenti collegamenti hash ...

$('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);
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.