Changer l'URL dans le navigateur sans charger la nouvelle page en utilisant JavaScript

297

Comment pourrais-je avoir une action JavaScript qui pourrait avoir des effets sur la page actuelle, mais changerait également l'URL dans le navigateur, donc si l'utilisateur frappe recharger ou mettre en signet, alors la nouvelle URL est utilisée?

Ce serait également bien si le bouton de retour rechargeait l'URL d'origine.

J'essaie d'enregistrer l'état JavaScript dans l'URL.

Steven Noble
la source
1
Ce serait tellement bien. Bien sûr, il serait limité aux modifications du même domaine. Mais un certain contrôle côté client du chemin (et pas seulement du hachage) est une étape logique maintenant que les rechargements de page sont une sorte de «dernier recours» pour de nombreuses applications.
harpo
1
Une "sorte" de bon usage de pushState:for(i=1;i<50;i++){var txt="..................................................";txt=txt.slice(0,i)+"HTML5"+txt.slice(i,txt.length);history.pushState({}, "html5", txt);}
Derek 朕 會 功夫
exemple de cet effet en action: dujour.com
Ben
2
Exemple de cet effet: facebook.com (lors de l'ouverture d'images dans la visionneuse)
C'est une bonne question. cependant, c'est une triste situation dans la mesure où si cette question avait été posée de cette manière aujourd'hui, elle aurait été déclassée et sautée par le "ce n'est pas le genre de site que vous demandez aux gens d'écrire tout votre code pour vous".
rosée

Réponses:

118

Avec HTML 5, utilisez la history.pushStatefonction . Par exemple:

<script type="text/javascript">
var stateObj = { foo: "bar" };
function change_my_url()
{
   history.pushState(stateObj, "page 2", "bar.html");
}
var link = document.getElementById('click');
link.addEventListener('click', change_my_url, false);
</script>

et un href:

<a href="#" id='click'>Click to change url to bar.html</a>

Si vous souhaitez modifier l'URL sans ajouter une entrée à la liste des boutons de retour, utilisez history.replaceStateplutôt.

clu3
la source
Vous avez raison, la dernière fois que j'ai testé Chrome. Je viens d'essayer avec Firefox 3.6 sur mon Ubuntu, cela n'a pas fonctionné. Je suppose que nous devrons attendre que HTML5 soit entièrement pris en charge dans FF
clu3
15
Un exemple de code est coupé et collé à partir de developer.mozilla.org/en/DOM/Manipulating_the_browser_history . Ce qui dérange en fait d'expliquer ce que foo et bar signifient dans ce cas.
mikemaccana
2
Le foo et la barre ne signifient rien, c'est un objet d'état défini arbitrairement que vous pourrez récupérer plus tard.
Paul Dixon
3
@Paul: oui, l'article ci-dessus fournit cette information.
mikemaccana
2
À compter de la publication de ce commentaire, il fonctionne sur Firefox Firefox et Safari. Pas de chance avec un safari mobile sur l'ipad ou l'iphone cependant :(
Sid
187

Si vous voulez qu'il fonctionne dans les navigateurs qui ne le prennent pas en charge, history.pushStateet history.popStatepourtant, la «vieille» façon est de définir l'identifiant du fragment, ce qui ne provoquera pas un rechargement de la page.

L'idée de base est de définir la window.location.hashpropriété sur une valeur qui contient les informations d'état dont vous avez besoin, puis d'utiliser l' événement window.onhashchange , ou pour les navigateurs plus anciens qui ne prennent pas en charge onhashchange(IE <8, Firefox <3.6), vérifiez périodiquement voir si le hachage a changé (en utilisant setIntervalpar exemple) et mettre à jour la page. Vous devrez également vérifier la valeur de hachage au chargement de la page pour configurer le contenu initial.

Si vous utilisez jQuery, il existe un plugin de changement de hachage qui utilisera la méthode prise en charge par le navigateur. Je suis sûr qu'il existe également des plugins pour d'autres bibliothèques.

Une chose à laquelle il faut faire attention est d'entrer en collision avec des identifiants sur la page, car le navigateur fera défiler jusqu'à n'importe quel élément avec un identifiant correspondant.

Matthew Crumley
la source
2
Les moteurs de recherche n'ignorent-ils pas ces liens internes (hachages)? Dans mon scénario, je veux que les araignées reprennent également ces liens.
Drew Noakes
3
@Drew, pour autant que je sache, les moteurs de recherche n'ont aucun moyen de traiter une partie d'une page comme un résultat distinct.
Matthew Crumley,
8
Il existe maintenant une proposition pour que les moteurs de recherche voient les hachages: googlewebmastercentral.blogspot.com/2009/10/…
LKM
5
Au lieu d'utiliser setIntervalpour inspecter l' document.location.hashun, vous pouvez utiliser l' hashchangeévénement, car il est beaucoup plus adopté. Vérifiez ici quels navigateurs prennent en charge chaque norme . Mon approche actuelle consiste à utiliser push / popState sur les navigateurs qui le prennent en charge. Sur les autres, j'ai défini le hachage et ne regarde que les hashchangenavigateurs compatibles. Cela laisse IE <= 7 sans support d'historique, mais avec un permalien utile. Mais qui se soucie de l'histoire d'IE <= 7?
Irae Carvalho
2
@gabeDel Oui, bien que je ne pense pas qu'Analytics enregistre le hachage, il aurait donc la même URL. Une meilleure façon serait d'appeler manuellement _trackPageviewavec l'URL "réelle" de la nouvelle page.
Matthew Crumley
37

window.location.href contient l'URL actuelle. Vous pouvez y lire, vous pouvez y ajouter, et vous pouvez le remplacer, ce qui peut entraîner un rechargement de page.

Si, comme cela semble, vous souhaitez enregistrer l'état javascript dans l'URL afin qu'il puisse être mis en signet, sans recharger la page, l'ajouter à l'URL actuelle après un # et avoir un morceau de javascript déclenché par l'événement onload analyser le courant URL pour voir si elle contient un état enregistré.

Si vous utilisez un? au lieu d'un #, vous forcerez un rechargement de la page, mais comme vous analyserez l'état enregistré au chargement, cela ne sera peut-être pas un problème; et cela fera également fonctionner les boutons avant et arrière.

l'ombre de la lune
la source
2
enregistrer l'état javascript dans l'URL est exactement ce que j'essaie de faire
Steven Noble
21

Je soupçonne fortement que ce n'est pas possible, car ce serait un problème de sécurité incroyable s'il l'était. Par exemple, je pourrais créer une page qui ressemble à une page de connexion à une banque et faire en sorte que l'URL dans la barre d'adresse ressemble à la vraie banque !

Peut-être que si vous expliquez pourquoi vous voulez faire cela, les gens pourraient suggérer des approches alternatives ...

[Modifier en 2011: depuis que j'ai écrit cette réponse en 2008, plus d'informations sont apparues concernant une technique HTML5 qui permet de modifier l'URL tant qu'elle est de la même origine]

Paul Dixon
la source
6
Pas vraiment un problème si les changements de non-rechargement étaient limités au chemin, à la chaîne de requête et au fragment, c'est-à-dire pas à l'autorité (domaine et autres).
Ollie Saunders
2
youtube.com/user/SilkyDKwan#p/u/2/gTfqaGYVfMg est un exemple qu'il est possible, essayez de changer de vidéo :)
Spidfire
Vous ne pouvez modifier que location.hash (tout après le #), comme le note Matthew Chumley dans la réponse acceptée.
Paul Dixon
2
Utilisez-vous facebook? Facebook modifie l'URL sans recharger, en utilisant history.pushState().
Derek 朕 會 功夫
Paul Dixon, vous devriez remarquer la boîte de réception Facebook, lorsque vous cliquez sur les noms sur le côté gauche, juste l'URL est modifiée et le nouveau chat est chargé dans la boîte de chat, la page n'est pas actualisée. il y a aussi beaucoup plus de sites sur WebGL, ils font de même
Dheeraj Thedijje
8

Les paramètres de sécurité du navigateur empêchent les utilisateurs de modifier directement l'URL affichée. Vous pourriez imaginer les vulnérabilités de phishing qui en résulteraient.

Le seul moyen fiable de modifier l'URL sans changer de page est d'utiliser un lien interne ou un hachage. par exemple: http://site.com/page.html devient http://site.com/page.html#item1 . Cette technique est souvent utilisée en hijax (AJAX + histoire de préservation).

Lorsque je fais cela, j'utilise souvent des liens pour les actions avec le hachage comme href, puis j'ajoute des événements de clic avec jquery qui utilisent le hachage demandé pour déterminer et déléguer l'action.

J'espère que cela vous met sur la bonne voie.

Jethro Larson
la source
Désolé mais votre réponse n'est pas vraie. Oui, vous pouvez modifier l'URL.
Derek 朕 會 功夫
1
Oui, vous pouvez utiliser l'api d'historique html5 mais ce n'est pas multi-navigateur, bien que vous puissiez utiliser un poly-fill qui se repliera sur les URL de hachage.
Jethro Larson
8

jQuery a un excellent plugin pour changer l'URL des navigateurs, appelé jQuery-pusher .

JavaScript pushStateet jQuery peuvent être utilisés ensemble, comme:

history.pushState(null, null, $(this).attr('href'));

Exemple:

$('a').click(function (event) {

  // Prevent default click action
  event.preventDefault();     

  // Detect if pushState is available
  if(history.pushState) {
    history.pushState(null, null, $(this).attr('href'));
  }
  return false;
});

Utiliser uniquement JavaScript history.pushState() , qui modifie le référent, qui est utilisé dans l'en-tête HTTP pour les objets XMLHttpRequest créés après avoir modifié l'état.

Exemple:

window.history.pushState("object", "Your New Title", "/new-url");

La méthode pushState ():

pushState()prend trois paramètres: un objet d'état, un titre (qui est actuellement ignoré) et (éventuellement) une URL. Examinons chacun de ces trois paramètres plus en détail:

  1. objet d'état - L'objet d'état est un objet JavaScript qui est associé à la nouvelle entrée d'historique créée par pushState(). Chaque fois que l'utilisateur accède au nouvel état, un événement popstate est déclenché et la propriété state de l'événement contient une copie de l'objet d'état de l'entrée d'historique.

    L'objet d'état peut être tout ce qui peut être sérialisé. Étant donné que Firefox enregistre les objets d'état sur le disque de l'utilisateur afin qu'ils puissent être restaurés après que l'utilisateur a redémarré son navigateur, nous imposons une limite de taille de 640 000 caractères à la représentation sérialisée d'un objet d'état. Si vous passez un objet d'état dont la représentation sérialisée est supérieure à celle-ci pushState(), la méthode lèvera une exception. Si vous avez besoin de plus d'espace que cela, nous vous encourageons à utiliser sessionStorage et / ou localStorage.

  2. title - Firefox ignore actuellement ce paramètre, bien qu'il puisse l'utiliser à l'avenir. Passer la chaîne vide ici devrait être à l'abri des modifications futures de la méthode. Alternativement, vous pouvez passer un titre court pour l'état vers lequel vous vous déplacez.

  3. URL - L' URL de la nouvelle entrée d'historique est donnée par ce paramètre. Notez que le navigateur ne tentera pas de charger cette URL après un appel à pushState(), mais il pourrait tenter de charger l'URL plus tard, par exemple après que l'utilisateur a redémarré son navigateur. La nouvelle URL n'a pas besoin d'être absolue; si elle est relative, elle est résolue par rapport à l'URL actuelle. La nouvelle URL doit être de la même origine que l'URL actuelle; sinon, pushState()lèvera une exception. Ce paramètre est facultatif; s'il n'est pas spécifié, il est défini sur l'URL actuelle du document.

Ilia Rostovtsev
la source
3

La galerie de photos de Facebook le fait en utilisant un #hash dans l'URL. Voici quelques exemples d'URL:

Avant de cliquer sur «suivant»:

/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507

Après avoir cliqué sur «suivant»:

/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507#!/photo.php?fbid=496435457507&set=a.218088072507.133423.681812507&pid=5887085&id=681812507

Notez le hash-bang (#!) Immédiatement suivi de la nouvelle URL.

Jonathon Hill
la source
2

Ce qui fonctionne pour moi, c'est - la history.replaceState()fonction qui est la suivante -

history.replaceState(data,"Title of page"[,'url-of-the-page']);

Cela ne rechargera pas la page, vous pouvez l'utiliser avec un événement javascript

Veshraj Joshi
la source
1

Je me demandais si cela serait possible tant que le chemin parent dans la page est le même, seulement quelque chose de nouveau y est ajouté.

Donc, disons que l'utilisateur est sur la page: http://domain.com/site/page.html alors le navigateur peut me laisser faire location.append = new.html et la page devient: http://domain.com/site/page.htmlnew.htmlet le navigateur ne la change pas.

Ou laissez simplement la personne changer le paramètre get, alors allons-y location.get = me=1&page=1.

La page d'origine devient donc http://domain.com/site/page.html?me=1&page=1et elle ne se rafraîchit pas.

Le problème avec # est que les données ne sont pas mises en cache (du moins je ne pense pas) lorsque le hachage est modifié. C'est donc comme à chaque fois qu'une nouvelle page est chargée, alors que les boutons Précédent et Suivant d'une page non Ajax sont capables de mettre en cache les données et ne passent pas de temps à recharger les données.

D'après ce que j'ai vu, l'historique de Yahoo charge déjà toutes les données à la fois. Il ne semble pas faire de requêtes Ajax. Ainsi, lorsque a divest utilisé pour gérer différentes heures supplémentaires de méthode, ces données ne sont pas stockées pour chaque état d'historique.

user58670
la source
1

mon code est:

//change address bar
function setLocation(curLoc){
    try {
        history.pushState(null, null, curLoc);
        return false;
    } catch(e) {}
        location.hash = '#' + curLoc;
}

et action:

setLocation('http://example.com/your-url-here');

et exemple

$(document).ready(function(){
    $('nav li a').on('click', function(){
        if($(this).hasClass('active')) {

        } else {
            setLocation($(this).attr('href'));
        }
            return false;
    });
});

C'est tout :)

Tusko Trush
la source
1

Une réponse plus simple que je présente,

window.history.pushState(null, null, "/abc")

cela s'ajoutera /abcaprès le nom de domaine dans l'URL du navigateur. Copiez simplement ce code et collez-le dans la console du navigateur et voyez l'URL passer à " https://stackoverflow.com/abc "

Sam
la source
0

J'ai eu du succès avec:

location.hash="myValue";

Il ajoute simplement #myValueà l'URL actuelle. Si vous devez déclencher un événement sur la page Charger, vous pouvez l'utiliser location.hashpour vérifier la valeur appropriée. N'oubliez pas de supprimer le #de la valeur retournée par location.hashexemple

var articleId = window.location.hash.replace("#","");
Adam Hey
la source