Comment puis-je mettre à jour window.location.hash sans sauter le document?

163

J'ai un panneau coulissant mis en place sur mon site Web.

Quand il a terminé l'animation, j'ai mis le hachage comme ça

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

(il s'agit d'un rappel, et le idest attribué plus tôt).

Cela fonctionne bien, pour permettre à l'utilisateur de mettre en signet le panneau, et aussi pour que la version non JavaScript fonctionne.

Cependant, lorsque je mets à jour le hachage, le navigateur saute à l'emplacement. Je suppose que c'est un comportement attendu.

Ma question est la suivante: comment puis-je éviter cela? Ie comment puis-je changer le hachage de la fenêtre, mais ne pas faire défiler le navigateur jusqu'à l'élément si le hachage existe? Une sorte event.preventDefault()de chose?

J'utilise jQuery 1.4 et le plugin scrollTo .

Merci beaucoup!

Mettre à jour

Voici le code qui change le panneau.

$('#something a').click(function(event) {
    event.preventDefault();
    var link = $(this);
    var id = link[0].hash;

    $('#slider').scrollTo(id, 800, {
        onAfter: function() {

            link.parents('li').siblings().removeClass('active');
            link.parent().addClass('active');
            window.location.hash = id;

            }
    });
});
Alex
la source
2
Je suppose que vous avez essayé event.preventDefault():)
Marko
@Marko je ne sais pas où le placer!
alex
Pouvez-vous publier le reste de votre code?
Marko
@Marko Ivanovski Je ne pense pas que ce soit pertinent, mais je vais voir ce que je peux faire.
alex
3
@Gareth Je ne pense pas qu'il y ait de place pour cela, car cela se produit dès que je mets à jour le hash.
alex

Réponses:

261

Il existe une solution de contournement en utilisant l'API d'historique sur les navigateurs modernes avec une solution de secours sur les anciens:

if(history.pushState) {
    history.pushState(null, null, '#myhash');
}
else {
    location.hash = '#myhash';
}

Le mérite revient à Lea Verou

Attila Fulop
la source
C'est la meilleure réponse, à mon avis.
Greg Annandale
10
Notez que pushState a pour effet secondaire (indenté) d'ajouter un état à la pile d'historique du navigateur. En d'autres termes, lorsque l'utilisateur clique sur le bouton Précédent, rien ne se passe à moins que vous n'ajoutiez également un écouteur d'événement popstate.
David Cook
32
@DavidCook - Nous pourrions également utiliser history.replaceState(ce qui, pour les changements de hachage, pourrait avoir plus de sens) pour éviter le besoin d'un popstateécouteur d'événements.
Jack
Cela a fonctionné pour moi. J'aime l'effet du changement d'historique. Je tiens à ajouter la mise en garde que cela ne déclenchera pas l' hashchangeévénement. C'était quelque chose que je devais contourner.
Jordan
avertissement! cela ne déclenchera pas d' hashchangeévénement
santiago arizti
52

Le problème est que vous définissez le window.location.hash sur l'attribut ID d'un élément. C'est le comportement attendu du navigateur pour accéder à cet élément, que vous "preventDefault ()" ou non.

Une façon de contourner ce problème est de préfixer le hachage avec une valeur arbitraire comme ceci:

window.location.hash = 'panel-' + id.replace('#', '');

Ensuite, tout ce que vous avez à faire est de vérifier le hachage préfixé lors du chargement de la page. En prime, vous pouvez même y faire défiler en douceur puisque vous contrôlez désormais la valeur de hachage ...

$(function(){
    var h = window.location.hash.replace('panel-', '');
    if (h) {
        $('#slider').scrollTo(h, 800);
    }
});

Si vous avez besoin que cela fonctionne à tout moment (et pas seulement lors du chargement initial de la page), vous pouvez utiliser une fonction pour surveiller les modifications de la valeur de hachage et accéder à l'élément correct à la volée:

var foundHash;
setInterval(function() {
    var h = window.location.hash.replace('panel-', '');
    if (h && h !== foundHash) {
        $('#slider').scrollTo(h, 800);
        foundHash = h;
    }
}, 100);
Derek Hunziker
la source
2
C'est la seule solution qui répond réellement à la question - autoriser le hachage sans sauter
amosmos
Cela répond vraiment à la question expliquant l'ajout et la suppression de la valeur arbitraire du hachage pour éviter le saut selon le comportement par défaut du navigateur.
lowtechsun
1
bonne solution, mais affaiblit la solution OP car elle annule l'utilisation de sections de signet
Samus
27

Solution bon marché et désagréable .. Utilisez le laid #! style.

Pour le régler:

window.location.hash = '#!' + id;

Pour le lire:

id = window.location.hash.replace(/^#!/, '');

Comme il ne correspond pas et ne s'ancre ou ne s'identifie pas dans la page, il ne sautera pas.

Gavin Brock
la source
3
J'ai dû préserver la compatibilité avec IE 7 et certaines versions mobiles du site. Cette solution était la plus propre pour moi. En général, je serais partout dans les solutions pushState.
Eric Goodwin
1
définir le hachage avec: window.location.hash = '#/' + id;puis le remplacer par: window.location.hash.replace(/^#\//, '#');embellira un peu l'url -> projects/#/tab1
braitsch
13

Pourquoi ne pas obtenir la position de défilement actuelle, la mettre dans une variable, puis affecter le hachage et remettre le défilement de la page là où il était:

var yScroll=document.body.scrollTop;
window.location.hash = id;
document.body.scrollTop=yScroll;

cela devrait fonctionner

utilisateur706270
la source
1
hm, cela fonctionnait pour moi pendant un certain temps, mais au cours des derniers mois, cela a cessé de fonctionner sur Firefox ....
matchew
10

J'ai utilisé une combinaison de la solution Attila Fulop (Lea Verou) pour les navigateurs modernes et de la solution Gavin Brock pour les anciens navigateurs comme suit:

if (history.pushState) {
    // IE10, Firefox, Chrome, etc.
    window.history.pushState(null, null, '#' + id);
} else {
    // IE9, IE8, etc
    window.location.hash = '#!' + id;
}

Comme l'observe Gavin Brock, pour récupérer l'identifiant, vous devrez traiter la chaîne (qui dans ce cas peut avoir ou non le "!") Comme suit:

id = window.location.hash.replace(/^#!?/, '');

Avant cela, j'ai essayé une solution similaire à celle proposée par user706270, mais elle ne fonctionnait pas bien avec Internet Explorer: comme son moteur Javascript n'est pas très rapide, vous pouvez remarquer l'augmentation et la diminution du scroll, ce qui produit un effet visuel désagréable.

aldemarcalazans
la source
2

Cette solution a fonctionné pour moi.

Le problème avec la configuration location.hashest que la page passera à cet identifiant s'il est trouvé sur la page.

Le problème avec window.history.pushStateest qu'il ajoute une entrée à l'historique pour chaque onglet sur lequel l'utilisateur clique. Ensuite, lorsque l'utilisateur clique sur leback bouton, il passe à l'onglet précédent. (c'est peut-être ce que vous voulez ou non. Ce n'était pas ce que je voulais).

Pour moi, replaceStatec'était la meilleure option en ce sens qu'elle ne remplace que l'historique actuel, donc lorsque l'utilisateur clique sur le backbouton, il passe à la page précédente.

$('#tab-selector').tabs({
  activate: function(e, ui) {
    window.history.replaceState(null, null, ui.newPanel.selector);
  }
});

Consultez la documentation de l'API History sur MDN.

Mike Vallano
la source
1

Je ne sais pas si vous pouvez modifier l'élément d'origine, mais que diriez-vous de passer de l'utilisation de l'id attr à quelque chose d'autre comme data-id? Ensuite, lisez simplement la valeur de data-id pour votre valeur de hachage et elle ne sautera pas.

Jon B
la source
1

Cette solution a fonctionné pour moi

// store the currently selected tab in the hash value
    if(history.pushState) {
        window.history.pushState(null, null, '#' + id);
    }
    else {
        window.location.hash = id;
    }

// on load of the page: switch to the currently selected tab
var hash = window.location.hash;
$('#myTab a[href="' + hash + '"]').tab('show');

Et mon code js complet est

$('#myTab a').click(function(e) {
  e.preventDefault();
  $(this).tab('show');
});

// store the currently selected tab in the hash value
$("ul.nav-tabs > li > a").on("shown.bs.tab", function(e) {
  var id = $(e.target).attr("href").substr(1);
    if(history.pushState) {
        window.history.pushState(null, null, '#' + id);
    }
    else {
        window.location.hash = id;
    }
   // window.location.hash = '#!' + id;
});

// on load of the page: switch to the currently selected tab
var hash = window.location.hash;
// console.log(hash);
$('#myTab a[href="' + hash + '"]').tab('show');
Rohit Dhiman
la source
0

Lors de l'utilisation du framework laravel, j'ai eu quelques problèmes avec l'utilisation d'une fonction route-> back () car elle effaçait mon hachage. Afin de conserver mon hachage, j'ai créé une fonction simple:

$(function() {   
    if (localStorage.getItem("hash")    ){
     location.hash = localStorage.getItem("hash");
    }
}); 

et je l'ai mis dans mon autre fonction JS comme ceci:

localStorage.setItem("hash", myvalue);

Vous pouvez nommer vos valeurs de stockage local comme vous le souhaitez; le mien nommé hash.

Par conséquent, si le hachage est défini sur PAGE1 et que vous accédez à PAGE2; le hachage sera recréé sur PAGE1 lorsque vous cliquez sur Retour sur PAGE2.

Andrew
la source