Défilement fluide lorsque vous cliquez sur un lien d'ancrage


488

J'ai quelques hyperliens sur ma page. Une FAQ que les utilisateurs liront lorsqu'ils visiteront ma section d'aide.

À l'aide de liens d'ancrage, je peux faire défiler la page vers l'ancre et y guider les utilisateurs.

Existe-t-il un moyen de rendre ce défilement fluide?

Mais notez qu'il utilise une bibliothèque JavaScript personnalisée. Peut-être que jQuery propose quelque chose comme ça cuit au four?


Pouvez-vous s'il vous plaît examiner la meilleure réponse peut-être? La solution en ligne pure css est difficile à trouver parmi toutes les suggestions jquery volumineuses: stackoverflow.com/a/51588820/1422553
Александр Киричек

Réponses:


1160

Mise à jour d'avril 2018: il existe désormais une manière native de procéder :

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

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

Ceci n'est actuellement pris en charge que dans les navigateurs les plus innovants.


Pour une prise en charge de navigateur plus ancienne, vous pouvez utiliser cette technique jQuery:

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

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

Et voici le violon: http://jsfiddle.net/9SDLw/


Si votre élément cible n'a pas d'ID et que vous y liez par son name, utilisez ceci:

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

    return false;
});

Pour des performances accrues, vous devez mettre en cache ce $('html, body')sélecteur, afin qu'il ne s'exécute pas à chaque fois qu'une ancre est cliquée:

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

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

    return false;
});

Si vous souhaitez que l'URL soit mise à jour, faites-le dans le animaterappel:

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
Cela semble supprimer la #extension de l'URL, brisant la fonction de retour. Y a-t-il un moyen de contourner ceci?
Fletch

2
@JosephSilber ne doit pas être que au scrollTop: $(this.hash).offset().toplieu de scrollTop: $(this.href).offset().top?
Gregory Pakosz

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

5
Je dirais que la mise en cache de l' html, bodyobjet ici n'est pas nécessaire, exécuter un sélecteur une fois par clic n'est pas vraiment beaucoup.

2
La première solution est la meilleure et la plus moderne, vous pouvez utiliser ce polyfill pour prendre en charge ce comportement sur les anciens navigateurs avec ce polyfill
Efe

166

La syntaxe correcte est:

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

Simplifier : SEC

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

Explication de href*=\\#:

  • *signifie qu'il correspond à ce qui contient #char. Ne correspondent donc qu'aux ancres . Pour en savoir plus sur la signification de cela, voir ici
  • \\est parce que le #est un caractère spécial dans le sélecteur css, nous devons donc y échapper.

8
je devais changer $('a')pour $('a[href*=#]')ne servir que les URL d'ancrage
okliv

2
@okliv Cela servira trop, par exemple un lien javascript comme <a href="javascript:$('#test').css('background-color', '#000')">Test</a>. Vous devriez plutôt utiliser $('a[href^=#]')pour faire correspondre toutes les URL commençant par un caractère de hachage.
Martin Braun

3
aussi, '#' est un caractère spécial et il doit être échappé comme ceci:a[href^=\\#]
QuinnFreedman

3
Cela a entraîné l'arrêt des liens vers les ancres des autres pages. Résolu en ajoutant un conditionnel if ($ ($ (this (hash) .selector) .length) {... scroll lisse. }
Liren

1
Comment puis-je l'animer lors de mon premier voyage vers une nouvelle page? Par exemple, en cliquant sur: website.com/newpage/#section2. Je voudrais qu'il charge la page puis défile vers le bas. Est-ce possible?
Samyer

72

La nouvelle actualité de CSS3. C'est beaucoup plus facile que toutes les méthodes répertoriées sur cette page et ne nécessite pas de Javascript. Entrez simplement le code ci-dessous dans votre CSS et tout d'un coup les liens pointent vers des emplacements à l'intérieur de votre propre page auront une animation de défilement fluide.

html{scroll-behavior:smooth}

Après cela, tout lien pointant vers un div glissera en douceur vers ces sections.

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

Edit: Pour ceux qui sont confus à propos de ce qui précède, une balise. Fondamentalement, c'est un lien cliquable. Vous pouvez alors avoir une autre balise div quelque part dans votre page Web comme

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

À cet égard, le lien a sera cliquable et ira à n'importe quelle section, dans ce cas, c'est notre div que nous avons appelé section.

BTW, j'ai passé des heures à essayer de le faire fonctionner. Trouvé la solution dans une section de commentaires obscurs. C'était buggé et ne fonctionnait pas dans certaines balises. N'a pas fonctionné dans le corps. Cela a finalement fonctionné lorsque je l'ai mis en html {} dans le fichier CSS.


4
Je peux être très pratique mais ce sont des inconvénients
Buzut

3
sympa, mais attention car pour le moment il n'est pas supporté par Safari et évidemment par Explorer (03/2019)
Marco Romano

2
belle solution, seule la couverture est limitée à 74,8%. peut-être à l'avenir
iepur1lla

1
Cet incroyable. Merci beaucoup.
Mikkel Fennefoss

1
Ce sera la réponse la plus pertinente dans les prochaines années.
Nurul Huda

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

cela a fonctionné parfaitement pour moi


1
"event.preventDefault ();" peut remplacer "return false;"
Andres Separ le

Désolé de le dire, mais ne fonctionne pas et ne s'affiche pas rapidement sur la page nommée ancre sans aucune fluidité.
Kamlesh

18

Je suis surpris que personne n'ait publié une solution native qui s'occupe également de mettre à jour le hachage d'emplacement du navigateur pour qu'il corresponde. C'est ici:

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

Voir le tutoriel: http://www.javascriptkit.com/javatutors/scrolling-html-bookmark-javascript.shtml

Pour les sites avec des en-têtes collants, scroll-padding-topCSS peut être utilisé pour fournir un décalage.


1
J'aime le plus cette réponse. Cependant, il n'y a aucun moyen de fournir un décalage. Comme cela serait nécessaire dans le cas d'un en-tête fixe.
bskool

Malheureusement, le même support médiocre que pour la propriété de comportement de défilement CSS: developer.mozilla.org/en-US/docs/Web/CSS/…
Dmitry Nevzorov

15

Uniquement CSS

html {
    scroll-behavior: smooth !important;
}

Tout ce dont vous avez besoin pour ajouter seulement cela. Maintenant, votre comportement de défilement des liens internes sera fluide comme un flux de flux.

Remarque : Tous les navigateurs les plus récents ( Opera, Chrome,Firefox etc) prend en charge cette fonctionnalité.

pour une compréhension détaillée, lisez cet article


1
agréable! Pourquoi n'est-ce pas la réponse acceptée? nous n'avons pas besoin de tout ce javascript!
Trevor de Koekkoek

1
Cela fonctionne très bien, cela devrait être la réponse acceptée.
tombeau

Vérifiez la prise en charge du navigateur ici
Ryan Zhou

1
ses œuvres comme un charme. pas besoin de js
Navbro

C'est la meilleure solution pour un défilement fluide JAMAIS! Merci!
yehanny

10

Je vous suggère de faire ce code générique:

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

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

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

return false;});

Vous pouvez voir un très bon article ici: jquery-effet-smooth-scroll-defilement-fluide


9
Ce n'est pas générique, c'est 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;
      }
    }
  });
});

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


1
cela ne semble fonctionner que pour les liens d'ancrage de page intérieure, mais les liens d'ancrage d'autres pages ne fonctionnent pas, par exemple website.com/about-us/#who-we-are
rainerbrunotte

5

Il y a déjà beaucoup de bonnes réponses ici - mais elles manquent toutes du fait que les ancres vides doivent être exclues . Sinon, ces scripts génèrent des erreurs JavaScript dès que l'on clique sur une ancre vide.

À mon avis, la bonne réponse est la suivante:

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

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

4

Utilisation de JQuery:

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


3

La réponse donnée fonctionne mais désactive les liens sortants. Ci-dessous une version avec un bonus supplémentaire facilité (swing) et respecte les liens sortants.

$(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 pour stop()cependant la miette d'url ne fonctionne pas comme prévu: le bouton Retour ne ramène pas, c'est parce que lorsque la miette est définie dans l'url après la fin de l'animation. C'est mieux sans une miette dans l'url, par exemple, c'est comme ça que fait airbnb.
Eric

3

HTML

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

ou avec URL complète absolue

<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

Les navigateurs modernes sont un peu plus rapides de nos jours. Un setInterval peut fonctionner. Cette fonction fonctionne bien dans Chrome et Firefox de nos jours. (Un peu lent en safari, n'a pas dérangé avec 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

Il existe un moyen css de le faire en utilisant scroll-behavior. Ajoutez la propriété suivante.

    scroll-behavior: smooth;

Et c'est tout. Aucun JS requis.

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: veuillez vérifier la compatibilité du navigateur.


dans quel conteneur dois-je utiliser scroll-behavior: smooth;
CraZyDroiD

En cas de doute, ajoutez-le à la balise body @CraZyDroiD
Santosh

2

Ajout de ceci:

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

annule en quelque sorte le décalage vertical

top - 72

dans Firefox et IE, mais pas dans Chrome. Fondamentalement, la page défile en douceur jusqu'au point où elle doit s'arrêter en fonction du décalage, mais saute ensuite à l'endroit où la page irait sans le décalage.

Il ajoute le hachage à la fin de l'URL, mais appuyer sur le bouton ne vous ramène pas en haut, il supprime simplement le hachage de l'URL et laisse la fenêtre de visualisation où il se trouve.

Voici le js complet que j'utilise:

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

Cette solution fonctionnera également pour les URL suivantes, sans rompre les liens d'ancrage vers différentes pages.

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

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

etc.

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

Je n'ai pas encore testé cela dans tous les navigateurs.


2

Cela permettra à jQuery de discerner facilement votre hachage cible et de savoir quand et où s'arrêter.

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

Code testé et vérifié

<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

J'ai fait cela pour les ancres href "/ xxxxx # asdf" et "#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

Voici la solution que j'ai implémentée pour plusieurs liens et ancres, pour un défilement fluide:

http://www.adriantomic.se/development/jquery-localscroll-tutorial/ si vous avez vos liens de navigation configurés dans une div de navigation et déclarés avec cette structure:

<a href = "#destinationA">

et vos destinations de balise d'ancrage correspondantes comme suit:

<a id = "destinationA">

Ensuite, chargez-le simplement dans la tête du document:

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

Merci à @Adriantomic


1

Si vous avez un simple bouton sur la page pour faire défiler vers le bas jusqu'à un div et que le bouton de retour fonctionne en sautant en haut, ajoutez simplement ce code:

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

Cela pourrait être étendu pour passer à différentes divisions également, en lisant la valeur de hachage et en faisant défiler comme Joseph Silbers répond.


1

N'oubliez jamais que la fonction offset () donne la position de votre élément à documenter. Ainsi, lorsque vous devez faire défiler votre élément par rapport à son parent, vous devez utiliser ceci;

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

Le point clé est d'obtenir scrollTop de scroll-div et de l'ajouter à scrollTop. Si vous ne le faites pas, la fonction position () vous donne toujours des valeurs de position différentes.


1

Vous pouvez utiliser window.scroll()avec behavior: smoothet topdéfinir le haut décalé de la balise d'ancrage, ce qui garantit que la balise d'ancrage sera en haut de la fenêtre.

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

Démo:

Vous pouvez simplement définir la propriété CSS scroll-behaviorà smooth(qui aide les navigateurs les plus modernes) qui évite la nécessité de Javascript.


0

merci pour le partage, Joseph Silber. Voici votre solution 2018 en tant qu'ES6 avec une modification mineure pour conserver le comportement standard (faites défiler vers le haut):

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

Nécessite jquery et anime pour ancrer la balise avec le nom spécifié au lieu de id, tout en ajoutant le hachage à l'URL du navigateur. Corrige également une erreur dans la plupart des réponses avec jquery où le signe # n'est pas préfixé par une barre oblique inversée. Le bouton de retour, malheureusement, ne revient pas correctement aux liens de hachage précédents ...

$('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);
});
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.