Question

Alors, maintenant que HTML5 history.pushState au changement l'histoire des navigateurs, sites commencent à utiliser ce en combinaison avec l'Ajax au lieu de changer l'identifiant de fragment de l'URL.

Malheureusement, cela signifie que ces appels ne peuvent pas être plus par onhashchange détecter.

Ma question est: Y at-il un moyen fiable;) pour détecter lorsqu'un site utilise history.pushState (Hack?)? La spécification ne rien d'état sur les événements qui sont élevés (au moins je ne pouvais pas trouver quoi que ce soit).
J'ai essayé de créer une façade et window.history remplacé par mon propre objet JavaScript, mais cela n'a pas d'effet du tout.

Des explications plus détaillées. Je développe un add-on Firefox qui doit détecter ces changements et agir en conséquence
Je sais qu'il y avait une question similaire il y a quelques jours qui ont demandé si l'écoute de certains événements DOM serait être efficace mais je me fierais plutôt pas parce que ces événements peuvent être générés pour beaucoup de raisons différentes.

Mise à jour:

Voici un jsFiddle (utilisez Firefox 4 ou Chrome 8) qui montre que onpopstate ne se déclenche pas quand pushState est appelé (ou suis-je en train de faire quelque chose de mal? ne hésitez pas à l'améliorer!).

Mise à jour 2:

Un autre problème (côté) est que window.location est pas mis à jour lors de l'utilisation pushState (mais je lu à ce sujet déjà ici donc je pense).

Était-ce utile?

La solution

  

5.5.9.1 définitions de l'événement

     

popstate événement est déclenché dans certains cas, lors de la navigation à une entrée d'historique de la session.

D'après cela, il n'y a aucune raison pour popstate être tiré lorsque vous utilisez pushState. Mais un événement tel que pushstate serait utile. Parce que history est un objet hôte, vous devez être prudent avec elle, mais Firefox semble être gentil dans ce cas. Ce code fonctionne très bien:

(function(history){
    var pushState = history.pushState;
    history.pushState = function(state) {
        if (typeof history.onpushstate == "function") {
            history.onpushstate({state: state});
        }
        // ... whatever else you want to do
        // maybe call onhashchange e.handler
        return pushState.apply(history, arguments);
    };
})(window.history);

Votre jsFiddle devient :

window.onpopstate = history.onpushstate = function(e) { ... }

Vous pouvez window.history.replaceState singe-patch de la même manière.

Remarque: Bien sûr, vous pouvez ajouter onpushstate simplement à l'objet global, et vous pouvez même en faire gérer plus d'événements via add/removeListener

Autres conseils

Vous pourriez lier à l'événement window.onpopstate?

https://developer.mozilla.org/en/DOM%3awindow.onpopstate

A partir de la documentation:

  

Un gestionnaire d'événement pour la popstate   événement sur la fenêtre.

     

Un événement popstate est envoyé au   fenêtre à chaque fois que l'histoire active   changements d'entrée. Si l'entrée d'historique   être activé a été créé par un appel   à history.pushState () ou a été affectée   par un appel à history.replaceState (),   la propriété de l'Etat d'événement popstate   contient une copie des années d'entrée de l'histoire   objet Etat.

Je l'habitude d'utiliser ceci:

var _wr = function(type) {
    var orig = history[type];
    return function() {
        var rv = orig.apply(this, arguments);
        var e = new Event(type);
        e.arguments = arguments;
        window.dispatchEvent(e);
        return rv;
    };
};
history.pushState = _wr('pushState'), history.replaceState = _wr('replaceState');

window.addEventListener('replaceState', function(e) {
    console.warn('THEY DID IT AGAIN!');
});

Il est presque le même que galambalazs fait.

Il est généralement exagéré cependant. Et il pourrait ne pas fonctionner dans tous les navigateurs. (Je me soucie seulement de ma version de mon navigateur.)

(Et il laisse un _wr var, de sorte que vous pouvez envelopper ou quelque chose. Je ne tenais pas à ce sujet.)

enfin trouvé la façon de le faire « correct »! Il faut ajouter un privilège à votre poste et en utilisant la page d'arrière-plan (pas seulement un script contenu), mais il fonctionne.

L'événement que vous voulez est browser.webNavigation.onHistoryStateUpdated, qui est déclenché lorsque une page utilise l'API history pour modifier l'URL. Il tire uniquement pour les sites que vous avez la permission d'accès, et vous pouvez également utiliser un filtre d'URL de réduire davantage sur le spam si vous avez besoin. Il nécessite l'autorisation de webNavigation (et de l'autorisation d'accueil de cours pour le domaine concerné (s)).

Le rappel d'événement obtient l'ID onglet, l'URL qui est « naviguait », et d'autres tels détails. Si vous devez prendre une action dans le script contenu sur cette page lorsque l'événement se déclenche, soit inject le script correspondant directement depuis la page d'arrière-plan, ou avoir le script contenu ouvrir un port à la page d'arrière-plan quand il charge, ont la page d'arrière-plan sauver ce port dans une collection indexée par onglet ID, et envoyer un message à travers le port concerné (à partir du script d'arrière-plan du script de contenu) lorsque l'événement se déclenche.

Puisque vous vous posez au sujet d'un addon Firefox, voici le code que je suis arrivé au travail. L'utilisation unsafeWindow est pas plus recommandé , et les erreurs quand pushState est appelé à partir d'un script client après avoir été modifié:

  

Autorisation refusée à la propriété d'accès history.pushState

Au lieu de cela, il y a une API appelée exportFunction qui permet à la fonction qui doit être injectée dans window.history comme suit:

var pushState = history.pushState;

function pushStateHack (state) {
    if (typeof history.onpushstate == "function") {
        history.onpushstate({state: state});
    }

    return pushState.apply(history, arguments);
}

history.onpushstate = function(state) {
    // callback here
}

exportFunction(pushStateHack, unsafeWindow.history, {defineAs: 'pushState', allowCallbacks: true});

Je pense que ce sujet a besoin d'une solution plus moderne.

Je suis sûr nsIWebProgressListener était autour de cette époque, je suis surpris personne ne l'a mentionné.

D'un framescript (pour e10s compatibilité):

let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW | Ci.nsIWebProgress.NOTIFY_LOCATION);

Ensuite, écoute dans le onLoacationChange

onLocationChange: function onLocationChange(webProgress, request, locationURI, flags) {
       if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT

Ce sera apparemment attraper de tous pushState. Mais il y a un avertissement de commentaires qu'il « déclenche aussi pour pushState ». Nous devons donc faire un peu plus de filtrage ici pour vous assurer qu'il est juste des choses pushState.

Basé sur: https: // github .com / jgraham / gecko / blob / 55d8d9aa7311386ee2dabfccb481684c8920a527 / boîte à outils / modules / addons / WebNavigation.jsm # L18

: ressources: //gre/modules/WebNavigationContent.js

Eh bien, je vois de nombreux exemples de remplacer la propriété pushState de history mais je ne suis pas sûr que c'est une bonne idée, je préfère créer un événement de service basé sur une API similaire à l'histoire de cette façon vous pouvez contrôler non seulement pousser l'État, mais remplacer l'état aussi bien et ses portes ouvertes pour beaucoup d'autres implémentations ne faisant pas appel à l'API de l'histoire mondiale. S'il vous plaît vérifier l'exemple suivant:

function HistoryAPI(history) {
    EventEmitter.call(this);
    this.history = history;
}

HistoryAPI.prototype = utils.inherits(EventEmitter.prototype);

const prototype = {
    pushState: function(state, title, pathname){
        this.emit('pushstate', state, title, pathname);
        this.history.pushState(state, title, pathname);
    },

    replaceState: function(state, title, pathname){
        this.emit('replacestate', state, title, pathname);
        this.history.replaceState(state, title, pathname);
    }
};

Object.keys(prototype).forEach(key => {
    HistoryAPI.prototype = prototype[key];
});

Si vous avez besoin de la définition EventEmitter, le code ci-dessus est basé sur l'émetteur d'événement NodeJS: https://github.com/nodejs/node/blob/36732084db9d0ff59b6ce31e839450cd91a156be/lib/events.js . la mise en œuvre de utils.inherits se trouve ici: https://github.com /nodejs/node/blob/36732084db9d0ff59b6ce31e839450cd91a156be/lib/util.js#L970

Comme norme indique:

  

Notez que simplement appeler history.pushState () ou history.replaceState () ne déclenche pas un événement popstate. L'événement popstate ne se déclenche en faisant une action du navigateur, comme en cliquant sur le bouton de retour (ou en appelant history.back () en JavaScript)

nous devons appeler history.back () pour trigeer WindowEventHandlers.onpopstate

insted de:

history.pushState(...)

faire:

history.pushState(...)
history.pushState(...)
history.back()
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top