¿Cómo deshabilitar el "salto" de anclaje al cargar una página?

110

Creo que esto puede no ser posible, intentaré explicarlo lo mejor que pueda. Tengo una página que contiene pestañas (con jquery), controladas por lo siguiente:

Estoy usando este código, proporcionado por otro usuario de una pregunta anterior.

<script type="text/javascript">
    $(function() {

     $('html, body').animate({scrollTop:0}); // this is my "fix"

        var tabContent = $(".tab_content");
        var tabs = $("#menu li");
        var hash = window.location.hash;
     tabContent.not(hash).hide();
        if(hash=="") {
      $('#tab1').fadeIn();
     }
        tabs.find('[href=' + hash + ']').parent().addClass('active');

        tabs.click(function() {
            $(this).addClass('active').siblings().removeClass('active');
            tabContent.hide();
            var activeTab = $(this).find("a").attr("href");

            $(activeTab).fadeIn();
           return false;
        });

    });
</script>

este código funciona muy bien cuando visito la página de "pestañas" directamente.

sin embargo, necesito vincular a pestañas individuales de otras páginas, por lo que para hacer esto, el código obtiene y window.location.hashluego muestra la pestaña apropiada.

la página no "salta" al ancla debido a "devolver falso".

Sin embargo, este evento solo se activa en un evento de clic. por lo tanto, si visito mis "pestañas" desde cualquier otra página, se activa el efecto "salto". Para combatir esto, me desplazo automáticamente a la parte superior de la página, pero preferiría que esto no sucediera.

¿Hay alguna forma de simular el "retorno falso" cuando se carga la página, evitando que se produzca el "salto" del ancla?

Espero que esté bastante claro.

Gracias

Ross
fuente

Respuestas:

143

¿Tu solución no funciona? No estoy seguro de haber entendido la pregunta correctamente. ¿Tiene una página de demostración? Tu podrías intentar:

if (location.hash) {
  setTimeout(function() {

    window.scrollTo(0, 0);
  }, 1);
}

Editar: probado y funciona en Firefox, IE y Chrome en Windows.
Edición 2: moverse setTimeout()dentro del ifbloque, apoyos @vsync.

dave1010
fuente
2
¡héroe! Sí, mi "corrección" funcionó, pero la página se "desplazaría" (como decía mi código), y preferiría que no se desplazara. tu código funciona perfectamente. No había mirado ScrollTo, ¿es siquiera necesaria la función? parece funcionar bien con solo window.scrollTo (0, 0);
Ross
3
Lo probé en Firefox en una página vacía sin el setTimeouty no siempre funcionó. Probablemente no lo necesite si está en jQuery de $(function() {todos modos. Realmente no necesita el location.hash, pero es bueno dejarlo para que los navegadores no tengan que hacer nada. ¡También te recuerda para qué sirve scrollTo!
dave1010
2
Hoy tuve este mismo problema. De hecho, tuve que agregar el hash (idéntico a los nombres de las pestañas / valores href) a mis enlaces de navegación. Terminé agregando un sufijo de 1 carácter a mis pestañas, y luego subcadenando los valores por 1 carácter (str.slice (0, -1) al compararlos con window.location.hash De esta manera, los hashes son diferentes y no se producen saltos .
developer10
1
El ifdebe haber fuera, no dentro. Además, el mínimo para el intervalo es de aproximadamente 10, por lo que 0 o 1 es lo mismo ... que 10. dependiendo del navegador.
vsync
2
Esto NO evita que el navegador salte al hashtag, debe poner un hashtag vacío para evitar el 'salto', vea mi respuesta aquí stackoverflow.com/a/29823913/826194
Larzan
43

Vea mi respuesta aquí: Respuesta de Stackoverflow

El truco consiste en eliminar el hashtag lo antes posible y almacenar su valor para su propio uso:

Es importante que no ponga esa parte del código en las funciones $ () o $ (ventana) .load (), ya que sería demasiado tarde y el navegador ya se movió a la etiqueta.

// store the hash (DON'T put this code inside the $() function, it has to be executed 
// right away before the browser can start scrolling!
var target = window.location.hash,
    target = target.replace('#', '');

// delete hash so the page won't scroll to it
window.location.hash = "";

// now whenever you are ready do whatever you want
// (in this case I use jQuery to scroll to the tag after the page has loaded)
$(window).on('load', function() {
    if (target) {
        $('html, body').animate({
            scrollTop: $("#" + target).offset().top
        }, 700, 'swing', function () {});
    }
});
Larzan
fuente
1
Me encontré con un error con FF donde estaba usando overflow-y de altura máxima en un div con una lista realmente larga, FF se desplazaría hacia abajo donde estaría el nodo oculto en el dom. Esto solo usó las tres líneas superiores del código real (no los comentarios) y esto corrigió mi problema con FireFox.
JQII
12

Hay otras formas de rastrear en qué pestaña estás; quizás estableciendo una cookie, o un valor en un campo oculto, etc., etc.

Yo diría que si no desea que la página se cargue, será mejor que use una de estas otras opciones en lugar del hash, porque la razón principal para usar el hash en lugar de ellas es permitir exactamente lo que desea quieres bloquear.

Otro punto: la página no saltará si sus enlaces hash no coinciden con los nombres de las etiquetas en el documento, por lo que quizás, si desea seguir usando el hash, podría manipular el contenido para que las etiquetas tengan un nombre diferente. Si usa un prefijo consistente, aún podrá usar Javascript para saltar entre ellos.

Espero que ayude.

Spudley
fuente
2
Buenos puntos. No olvide intentar mantener la página utilizable / accesible con JS / CSS desactivado.
dave1010
12

Usé la solución de dave1010, pero estaba un poco nervioso cuando la puse dentro de la función $ (). Ready. Entonces hice esto: (no dentro del $ (). Listo)

    if (location.hash) {               // do the test straight away
        window.scrollTo(0, 0);         // execute it straight away
        setTimeout(function() {
            window.scrollTo(0, 0);     // run it a bit later also for browser compatibility
        }, 1);
    }
desequilibrado
fuente
10
  1. El navegador salta a <element id="abc" />si hay http://site.com/#abc hash en la dirección y el elemento con id = "abc" es visible. Por lo tanto, sólo debe ocultar el elemento por defecto: <element id="anchor" style="display: none" />. Si no hay un elemento visible con id = hash en la página, el navegador no saltará.

  2. Cree un script que verifique el hash en la URL y luego busque y muestre el elemento apropiado (que está oculto por defecto).

  3. Deshabilite el comportamiento predeterminado de "enlace tipo hash" vinculando un evento onclick a un enlace y especifique return false;como resultado del evento onclick.

Cuando implementé pestañas, escribí algo así:

    <script>
    $(function () {         
        if (location.hash != "") {
            selectPersonalTab(location.hash);
        }
        else {
            selectPersonalTab("#cards");
        }
    });

    function selectPersonalTab(hash) {
        $("#personal li").removeClass("active");
        $("a[href$=" + hash + "]").closest("li").addClass("active");
        $("#personaldata > div").hide();
        location.hash = hash;
        $(hash).show();
    }

    $("#personal li a").click(function (e) {
        var t = e.target;
        if (t.href.indexOf("#") != -1) {
            var hash = t.href.substr(t.href.indexOf("#"));
            selectPersonalTab(hash);
            return false;
        }
    });
</script>
Sergei Smirnov
fuente
6

Ninguna de las respuestas no funciona lo suficientemente bien para mí, veo que la página salta para anclar y luego para arriba para algunas soluciones, algunas respuestas no funcionan en absoluto, puede que las cosas hayan cambiado durante años. Espero que mi función ayude a alguien.

/**
 * Prevent automatic scrolling of page to anchor by browser after loading of page.
 * Do not call this function in $(...) or $(window).on('load', ...),
 * it should be called earlier, as soon as possible.
 */
function preventAnchorScroll() {
    var scrollToTop = function () {
        $(window).scrollTop(0);
    };
    if (window.location.hash) {
        // handler is executed at most once
        $(window).one('scroll', scrollToTop);
    }

    // make sure to release scroll 1 second after document readiness
    // to avoid negative UX
    $(function () {
        setTimeout(
            function () {
                $(window).off('scroll', scrollToTop);
            },
            1000
        );
    });
}
kdmitry
fuente
La mejor solución que encontré hasta ahora. Y de hecho vi mucho.
MarkSkayff
¡Gracias por esto! También es la única solución que funcionó para mí después de horas de intentos, pero la mejor parte de esta solución para mí es que no tengo que eliminar el hash.
Chris Vecchio
Esto no funciona si el desplazador ya está en la parte superior de la página al cargar la página, entonces se desplazará hacia abajo como de costumbre y una vez que la página esté lista, saltará a la parte superior. ¿Alguien sabe cómo es este el caso?
robgha01
@ robgha01, asegúrese de que la función se ejecute lo antes posible y no espere ningún evento, esto es incorrecto: $(document).ready(function () { preventAnchorScroll(); });simplemente llame a la función al comienzo de su JavaScript. Lo probé ahora, funciona para mí en Chrome, Firefox y Opera.
kdmitry
4

CSS sucio solo se corrige para permanecer desplazado hacia arriba cada vez que se usa el ancla (no es útil si aún desea usar anclajes para saltos de desplazamiento, muy útil para enlaces profundos):

.elementsWithAnchorIds::before {
   content: "";
   display: block;
   height: 9999px;
   margin-top: -9999px; //higher thin page height
}
otro tu
fuente
taaaaan inteligente!
duhaime
3

Si bien la respuesta aceptada funciona bien, descubrí que a veces, especialmente en páginas que contienen imágenes grandes, la barra de desplazamiento saltará salvajemente usando

 window.scrollTo(0, 0);

Lo que puede distraer bastante al usuario.

La solución con la que me decidí al final es bastante simple, y es usar una identificación diferente en el objetivo a la que se usó en location.hash

P.ej:

Aquí está el enlace en alguna otra página.

<a href="/page/with/tabs#tab2">Some info found in tab2 on tabs page</a>

Entonces, por supuesto, si hay un elemento con un ID de tab2 en la página de pestañas, la ventana saltará a él al cargar.

Entonces puede agregar algo a la identificación para evitarlo:

<div id="tab2-noScroll">tab2 content</div>

Y a continuación, puede añadir "-noScroll"a location.hashla javascript:

<script type="text/javascript">
    $(function() {

        var tabContent = $(".tab_content");
        var tabs = $("#menu li");
        var hash = window.location.hash;


     tabContent.not(hash + '-noScroll').hide();                           
        if(hash=="") {       //^ here
      $('#tab1-noScroll').fadeIn();
     }
        tabs.find('[href=' + hash + ']').parent().addClass('active');

        tabs.click(function() {
            $(this).addClass('active').siblings().removeClass('active');
            tabContent.hide();
            var activeTab = $(this).find("a").attr("href") + '-noScroll'; 
                                                               //^ and here

            $(activeTab).fadeIn();
           return false;
        });

    });
</script>
Andrés
fuente
2

En mi caso, debido al uso de un enlace de anclaje para la ventana emergente CSS, lo he usado de esta manera:

Simplemente guarde el pageYOffset primero al hacer clic y luego configure ScrollTop en eso:

$(document).on('click','yourtarget',function(){
        var st=window.pageYOffset;
        $('html, body').animate({
                'scrollTop' : st
            });
});
Metro Polisue
fuente
Después de probar las otras soluciones, esta fue la que funcionó para mí, en mi caso, el sitio web con el que estaba trabajando usó el # en la URL para cargar una sección específica de un pago. En lugar de modificar la funcionalidad de pago para el hash en la URL, acabo de usar esto. Gracias
chris c
1

Creo que encontré una forma de usar las pestañas de la interfaz de usuario sin rehacer la funcionalidad. Hasta ahora no he encontrado ningún problema.

    $( ".tabs" ).tabs();
    $( ".tabs" ).not(".noHash").bind("tabsshow", function(event, ui) { 
        window.location.hash = "tab_"+ui.tab.hash.replace(/#/,"");
        return false;
    });

    var selectedTabHash = window.location.hash.replace(/tab_/,"");
    var index = $( ".tabs li a" ).index($(".tabs li a[href='"+selectedTabHash+"']"));
    $( ".tabs" ).not(".noHash").tabs('select', index);

Todo dentro de un evento listo. Antepone "tab_" para el hash, pero funciona tanto cuando se hace clic como cuando se carga la página con hash.

JRomero
fuente
1

Esto funcionará con bootstrap v3

$(document).ready(function() {
  if ($('.nav-tabs').length) {
    var hash = window.location.hash;
    var hashEl = $('ul.nav a[href="' + hash + '"]');
    hash && hashEl.tab('show');

    $('.nav-tabs a').click(function(e) {
      e.preventDefault();
      $(this).tab('show');
      window.location.hash = this.hash;
    });

    // Change tab on hashchange
    window.addEventListener('hashchange', function() {
      var changedHash = window.location.hash;
      changedHash && $('ul.nav a[href="' + changedHash + '"]').tab('show');
    }, false);
  }
});
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>
<div class="container">
<ul class="nav nav-tabs">
  <li class="active"><a data-toggle="tab" href="#home">Home</a></li>
  <li><a data-toggle="tab" href="#menu1">Menu 1</a></li>
</ul>

<div class="tab-content">
  <div id="home" class="tab-pane fade in active">
    <h3>HOME</h3>
    <p>Some content.</p>
  </div>
  <div id="menu1" class="tab-pane fade">
    <h3>Menu 1</h3>
    <p>Some content in menu 1.</p>
  </div>
</div>
</div>

Lasithds
fuente
0

Otro enfoque

Intente verificar si la página se ha desplazado y solo luego restablezca la posición:

var scrolled = false;

$(window).scroll(function(){
  scrolled = true;
});

if ( window.location.hash && scrolled ) {
  $(window).scrollTop( 0 );
}

Heres a demo

hitautodestruct
fuente
no funciona hermano ... la carga inicial está en la parte inferior ... después de la actualización se mantiene en la parte superior
Martin Zvarík
0

No tuve mucho éxito con los setTimeoutmétodos anteriores en Firefox.

En lugar de setTimeout, he usado una función de carga:

window.onload = function () {
    if (location.hash) {
        window.scrollTo(0, 0);
    }
};

Lamentablemente, sigue teniendo muchos fallos.

bozdoz
fuente
porque la carga se ejecuta después de que todo está completamente cargado, incluidas las fotos ... así que, por supuesto, es un problema de esta manera
Martin Zvarík
0

No he tenido ningún éxito constante con estas soluciones.

Aparentemente debido al hecho de que el salto ocurre antes de Javascript => referencia ( http://forum.jquery.com/topic/preventing-anchor-jump )

Mi solución es colocar todos los anclajes en la parte superior de forma predeterminada con CSS.

.scroll-target{position:fixed;top:0;left:0;}

Luego use jquery para desplazarse al padre del ancla de destino, o un hermano (tal vez una etiqueta em vacía)

<a class="scroll-target" name="section3"></a><em>&nbsp</em>

Ejemplo de jquery para desplazarse cuando la URL se ingresa con hash
(la página no debe estar ya cargada en la ventana / pestaña, esto cubre enlaces de otras fuentes / externas)

setTimeout(function() {
    if (window.location.hash) {               
        var hash = window.location.hash.substr(1);   
        var scrollPos = $('.scroll-target[name="'+hash+'"]').siblings('em').offset().top; 
        $("html, body").animate({ scrollTop: scrollPos }, 1000);    
    }
}, 1);

También querrá evitar los clics de anclaje predeterminados mientras está en la página y luego desplazarse a sus objetivos

<a class="scroll-to" href="#section3">section three</a>

y jquery

$('a.scroll-to').click(function(){
    var target = $(this).attr('href').substr(1);
    var scrollPos = $('.scroll-target[name="'+target+'"]').siblings('em').offset().top; 
    $("html, body").animate({ scrollTop: scrollPos }, 1000);
    return false;
});

Lo bueno de este método son los destinos de la etiqueta de anclaje, que permanecen estructuralmente al lado del contenido relevante, aunque su posición CSS está en la parte superior.

Esto debería significar que los rastreadores de los motores de búsqueda no tendrán ningún problema.

Saludos, espero que esto ayude a
Gray

grayskale.com
fuente
0

Intente esto para evitar que el cliente realice cualquier tipo de desplazamiento vertical en hashchanged (dentro de su controlador de eventos):

var sct = document.body.scrollTop;
document.location.hash = '#next'; // or other manipulation
document.body.scrollTop = sct;

(redibujar navegador)

Andy
fuente
0

Resolví mi promlem haciendo esto:

// navbar height 
var navHeigth = $('nav.navbar').height();    

// Scroll to anchor function
var scrollToAnchor = function(hash) {
  // If got a hash
  if (hash) {
    // Scroll to the top (prevention for Chrome)
    window.scrollTo(0, 0);
    // Anchor element
    var term = $(hash);

    // If element with hash id is defined
    if (term) {

      // Get top offset, including header height
      var scrollto = term.offset().top - navHeigth;

      // Capture id value
      var id = term.attr('id');
      // Capture name value
      var name = term.attr('name');

      // Remove attributes for FF scroll prevention
      term.removeAttr('id').removeAttr('name');
      // Scroll to element
      $('html, body').animate({scrollTop:scrollto}, 0);

      // Returning id and name after .5sec for the next scroll
      setTimeout(function() {
        term.attr('id', id).attr('name', name);
      }, 500);
    }
  }
};

// if we are opening the page by url
if (location.hash) {
  scrollToAnchor(location.hash);
}

// preventing default click on links with an anchor
$('a[href*="#"]').click(function(e) {
  e.preventDefault();
  // hash value
  var hash = this.href.substr(this.href.indexOf("#"));
  scrollToAnchor(hash);
});`
xottabych007
fuente