web-dev-qa-db-fra.com

Comment puis-je récupérer si l'événement popstate provient d'actions précédentes ou antérieures avec l'état push HTML5?

Je développe une page Web où, en fonction des actions suivantes ou précédentes, je fais l'animation correspondante, le problème survient lors de l'utilisation du pushstate. Lorsque je reçois l'événement, comment savoir si l'utilisateur a cliqué sur les boutons d'historique en arrière ou en avant à l'aide de l'API Pushstate?, Ou dois-je implémenter quelque chose moi-même?

55
Davsket

Vous devez l'implémenter vous-même, ce qui est assez simple.

  • Lorsque vous appelez pushState, donnez à l'objet de données un identifiant d'incrémentation unique (uid).
  • Lorsque onpopstate handler est invoqué; vérifier l'uid d'état par rapport à une variable persistante contenant le dernier uid d'état.
  • Mettez à jour la variable persistante avec l'uid de l'état actuel.
  • Effectuez différentes actions selon que l'uid d'état était supérieur ou inférieur au dernier uid d'état.
56
bennedich

Cette réponse doit fonctionner avec une application Push-state d'une seule page, ou une application multi-pages, ou une combinaison des deux. (Corrigé pour corriger le History.length bug corrigé dans le commentaire de Mesqualito.)

Comment ça fonctionne

Nous pouvons facilement écouter les nouvelles entrées de la pile d'historique. Nous savons que pour chaque nouvelle entrée, la spécification nécessite que le navigateur:

  1. "Supprimer toutes les entrées de l'historique de session du contexte de navigation après l'entrée actuelle"
  2. "Ajoutez une nouvelle entrée à la fin"

Au moment de l'entrée, donc:

nouvelle position d'entrée = dernière position affichée + 1

La solution est alors:

  1. Tamponner chaque entrée d'historique avec sa propre position dans la pile
  2. Gardez une trace dans la mémoire de session de la dernière position affichée
  3. Découvrez le sens du voyage en comparant les deux

Exemple de code

function reorient() // After travelling in the history stack
{
    const positionLastShown = Number( // If none, then zero
      sessionStorage.getItem( 'positionLastShown' ));
    let position = history.state; // Absolute position in stack
    if( position === null ) // Meaning a new entry on the stack
    {
        position = positionLastShown + 1; // Top of stack

        // (1) Stamp the entry with its own position in the stack
        history.replaceState( position, /*no title*/'' );
    }

    // (2) Keep track of the last position shown
    sessionStorage.setItem( 'positionLastShown', String(position) );

    // (3) Discover the direction of travel by comparing the two
    const direction = Math.sign( position - positionLastShown );
    console.log( 'Travel direction is ' + direction );
      // One of backward (-1), reload (0) or forward (1)
}

addEventListener( 'pageshow', reorient );
addEventListener( 'popstate', reorient ); // Travel in same page

Voir aussi live copy du code.

Limitation

Cette solution ignore les entrées d'historique des pages externes, étrangères à l'application, comme si l'utilisateur ne les avait jamais visitées. Il calcule le sens de déplacement uniquement par rapport à la dernière page d'application affichée, quelle que soit la page externe visitée entre les deux. Si vous vous attendez à ce que l'utilisateur envoie des entrées étrangères dans la pile (voir le commentaire d'Atomosk), vous devrez peut-être une solution de contournement.

3
Michael Allan