Desplazamiento suave al hacer clic en un enlace de anclaje


488

Tengo un par de hipervínculos en mi página. Preguntas frecuentes que los usuarios leerán cuando visiten mi sección de ayuda.

Utilizando enlaces de anclaje, puedo hacer que la página se desplace hacia el ancla y guiar a los usuarios allí.

¿Hay alguna manera de hacer que ese desplazamiento sea suave?

Pero tenga en cuenta que está usando una biblioteca JavaScript personalizada. ¿Quizás jQuery ofrece algo como esto horneado?


¿Puedes por favor revisar la mejor respuesta tal vez? La solución de una sola línea css pura es difícil de encontrar entre todas las sugerencias de jquery voluminosas: stackoverflow.com/a/51588820/1422553
Александр Киричек

Respuestas:


1160

Actualización de abril de 2018: ahora hay una forma nativa de hacer esto :

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

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

Actualmente, esto solo se admite en los navegadores más avanzados.


Para soporte de navegador más antiguo, puede usar esta técnica jQuery:

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

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

Y aquí está el violín: http://jsfiddle.net/9SDLw/


Si su elemento de destino no tiene una ID, y se está vinculando a él por él name, use esto:

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

    return false;
});

Para aumentar el rendimiento, debe almacenar en caché ese $('html, body')selector, para que no se ejecute cada vez que se hace clic en un ancla:

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

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

    return false;
});

Si desea que se actualice la URL, hágalo dentro de la animatedevolución de llamada:

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
Esto parece eliminar la # extensión de la URL, rompiendo la función de retroceso. ¿Hay alguna forma de evitar esto?
Fletch

2
@JosephSilber no debería ser eso en scrollTop: $(this.hash).offset().toplugar de scrollTop: $(this.href).offset().top?
Gregory Pakosz

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

55
Yo diría que el almacenamiento en caché del html, bodyobjeto aquí es innecesario, ejecutar un selector una vez por clic no es realmente mucho.

2
La primera solución es la mejor y más moderna, puede usar este polyfill para admitir este comportamiento en navegadores antiguos con este polyfill
Efe

166

La sintaxis correcta es:

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

Simplificando : SECO

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

Explicación de href*=\\#:

  • *significa que coincide con lo que contiene #char. Por lo tanto, solo coinciden con las anclas . Para más información sobre el significado de esto, vea aquí
  • \\es porque #es un selector especial de caracteres en CSS, así que tenemos que escapar de él.

8
tuve que cambiar $('a')a $('a[href*=#]')para servir solo URL de anclaje
okliv

2
@okliv Esto servirá demasiado, por ejemplo, un enlace de JavaScript como <a href="javascript:$('#test').css('background-color', '#000')">Test</a>. Debería usar $('a[href^=#]')para hacer coincidir todas las URL que comienzan con un carácter hash.
Martin Braun

3
también, '#' es un personaje especial y necesita ser escapado así:a[href^=\\#]
QuinnFreedman

3
Esto provocó que los enlaces a las anclas en otras páginas dejaran de funcionar. Se solucionó agregando un condicional if ($ ($ (this.hash) .selector) .length) {... desplazamiento suave. }
Liren

1
¿Cómo puedo animar esto cuando viajo por primera vez a una nueva página? Por ejemplo, haciendo clic en: website.com/newpage/#section2. Me gustaría cargar la página y luego desplazarme hacia abajo. ¿Es eso posible?
Samyer

72

La nueva actualidad en CSS3. Esto es mucho más fácil que todos los métodos enumerados en esta página y no requiere Javascript. Simplemente ingrese el siguiente código en su CSS y de repente los enlaces apuntan a ubicaciones dentro de su propia página que tendrán una animación de desplazamiento suave.

html{scroll-behavior:smooth}

Después de eso, cualquier enlace que apunte hacia un div se deslizará suavemente hacia esas secciones.

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

Editar: Para aquellos confundidos acerca de lo anterior, una etiqueta. Básicamente es un enlace en el que se puede hacer clic. Luego puede tener otra etiqueta div en algún lugar de su página web como

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

A este respecto, se podrá hacer clic en el enlace e irá a la sección # que sea, en este caso es nuestra sección div a la que llamamos.

Por cierto, pasé horas tratando de hacer que esto funcione. Encontré la solución en alguna sección de comentarios oscuros. Tenía errores y no funcionaría en algunas etiquetas. No funcionó en el cuerpo. Finalmente funcionó cuando lo puse en html {} en el archivo CSS.


44
Puedo ser muy útil pero son inconvenientes
Buzut

3
bueno, pero ten cuidado porque por el momento no es compatible con Safari y obviamente con Explorer (03/2019)
Marco Romano

2
buena solución, solo la cobertura es con 74,8% limitada. tal vez en el futuro
iepur1lla

1
Que increible. Muchas gracias.
Mikkel Fennefoss

1
Esta será la respuesta más relevante en los próximos años.
Nurul Huda

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

esto funcionó perfecto para mí


1
"event.preventDefault ();" puede reemplazar "return false";
Andres Separ

Lamento decirlo, pero no funciona y aparece en la página llamada ancla rápidamente sin ninguna suavidad.
Kamlesh

18

Me sorprende que nadie haya publicado una solución nativa que también se encargue de actualizar el hash de ubicación del navegador para que coincida. Aquí está:

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

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

Para los sitios con encabezados fijos, scroll-padding-topse puede usar CSS para proporcionar un desplazamiento.


1
Me gusta más esta respuesta. Sin embargo, afais no hay forma de proporcionar una compensación. Como sería necesario en el caso de un encabezado fijo.
bskool

Desafortunadamente, el mismo pobre soporte que para la propiedad de comportamiento de desplazamiento CSS: developer.mozilla.org/en-US/docs/Web/CSS/…
Dmitry Nevzorov

15

Solo CSS

html {
    scroll-behavior: smooth !important;
}

Todo lo que necesitas es agregar solo esto. Ahora su comportamiento de desplazamiento de enlaces internos será suave como un flujo de flujo.

Nota : Todos los navegadores más recientes ( Opera, Chrome,Firefox etc.) compatible con esta función.

para una comprensión detallada, lea este artículo


1
¡bonito! ¿Por qué esta no es la respuesta aceptada? ¡no necesitamos todo ese javascript!
Trevor de Koekkoek

1
Funciona muy bien, esta debería ser la respuesta aceptada.
tumba

Consulte el soporte del navegador aquí
Ryan Zhou

1
Funciona como un encanto. no hay necesidad de js
Navbro

¡Esta es la mejor solución para un desplazamiento suave NUNCA! ¡Gracias!
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;
      }
    }
  });
});

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


1
esto solo parece funcionar para los enlaces de anclaje de la página interna, pero los enlaces de anclaje de otras páginas no funcionan, por ejemplo website.com/about-us/#who-we-are
rainerbrunotte

5

Aquí ya hay muchas buenas respuestas, sin embargo, a todas les falta el hecho de que las anclas vacías deben ser excluidas . De lo contrario, esos scripts generan errores de JavaScript tan pronto como se hace clic en un ancla vacía.

En mi opinión, la respuesta correcta es la siguiente:

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

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

4

Usando JQuery:

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


3

La respuesta dada funciona pero deshabilita los enlaces salientes. Debajo de una versión con una bonificación adicional de salida (swing) y respeta los enlaces salientes.

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

stop()Sin embargo, 1 para la migaja de url no funciona como se esperaba: el botón Atrás no regresa, esto es porque cuando la miga se establece en la url después de que se completa la animación. Es mejor sin una miga en la url, por ejemplo, así es como funciona airbnb.
Eric

3

HTML

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

o con URL completa absoluta

<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

Los navegadores modernos son un poco más rápidos en estos días. Un setInterval podría funcionar. Esta función funciona bien en Chrome y Firefox en estos días. (Un poco lento en safari, no se molestó 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

Hay una manera css de hacerlo usando el comportamiento de desplazamiento. Agregue la siguiente propiedad.

    scroll-behavior: smooth;

Y eso es todo. No se requiere 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>

PD: compruebe la compatibilidad del navegador.


a qué contenedor debo usar el comportamiento de desplazamiento: suave;
CraZyDroiD

En caso de duda, agréguelo a la etiqueta del cuerpo @CraZyDroiD
Santosh

2

Agregando esto:

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

está de alguna manera anulando el desplazamiento vertical

top - 72

en Firefox e IE, pero no en Chrome. Básicamente, la página se desplaza suavemente hasta el punto en el que debería detenerse según el desplazamiento, pero luego salta hacia donde iría la página sin el desplazamiento.

Agrega el hash al final de la url, pero presionar hacia atrás no lo lleva de regreso a la parte superior, solo elimina el hash de la url y deja la ventana de visualización donde se encuentra.

Aquí está el js completo que estoy 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

Esta solución también funcionará para las siguientes URL, sin romper los enlaces de anclaje a diferentes páginas.

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

Todavía no he probado esto en todos los navegadores.


2

Esto facilitará que jQuery pueda discernir su hash objetivo y saber cuándo y dónde detenerse.

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

Código probado y verificado

<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

Hice esto para los anclajes href "/ xxxxx # asdf" y "#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

Aquí está la solución que implementé para múltiples enlaces y anclajes, para un desplazamiento suave:

http://www.adriantomic.se/development/jquery-localscroll-tutorial/ si tiene sus enlaces de navegación configurados en un div de navegación y declarados con esta estructura:

<a href = "#destinationA">

y sus destinos de etiqueta de anclaje correspondientes de la siguiente manera:

<a id = "destinationA">

Luego simplemente cargue esto en el encabezado 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>

Gracias a @Adriantomic


1

Si tiene un botón simple en la página para desplazarse hacia abajo hasta un div y desea que el botón Atrás salte al inicio , simplemente agregue este código:

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

Esto podría extenderse para saltar también a diferentes divs, leyendo el valor hash y desplazándose como responde Joseph Silbers.


1

Nunca olvide que la función offset () está dando la posición de su elemento para documentar. Entonces, cuando necesite desplazar su elemento en relación con su elemento primario, debe usar esto;

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

El punto clave es obtener scrollTop de scroll-div y agregarlo a scrollTop. Si no lo haces, la función position () siempre te da diferentes valores de posición.


1

Se puede utilizar window.scroll()con behavior: smoothy topfijado a la parte superior de desplazamiento de la etiqueta de anclaje que asegura que la etiqueta de anclaje estará en la parte superior de la ventana.

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

Manifestación:

Simplemente puede establecer la propiedad CSS scroll-behavioren smooth(que es compatible con la mayoría de los navegadores modernos) que evita la necesidad de Javascript.


0

Gracias por compartir, Joseph Silber. Aquí su solución 2018 como ES6 con un cambio menor para mantener el comportamiento estándar (desplazarse hacia arriba):

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

Requiere jquery y anima a anclar la etiqueta con el nombre especificado en lugar de id, mientras agrega el hash a la URL del navegador. También corrige un error en la mayoría de las respuestas con jquery donde el signo # no tiene como prefijo una barra invertida de escape. El botón de retroceso, desafortunadamente, no regresa correctamente a los enlaces hash anteriores ...

$('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);
});
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.