web-dev-qa-db-fra.com

Evénement lorsque window.location.href change

J'écris un script Greasemonkey pour un site qui modifie à un moment donné location.href.

Comment puis-je obtenir un événement (via window.addEventListener ou quelque chose de similaire) quand window.location.href changements sur une page? J'ai également besoin d'accéder au DOM du document pointant sur l'URL nouvelle/modifiée.

J'ai déjà vu d'autres solutions qui impliquent des délais d'attente et des scrutations, mais j'aimerais éviter cela si possible.

57
Johan Dahlin

événement popstate :

L'événement popstate est déclenché lorsque l'entrée d'historique active est modifiée. [...] L'événement popstate est uniquement déclenché par une action du navigateur, telle qu'un clic sur le bouton Précédent (ou l'appel de history.back () en JavaScript).

Donc, écouter popstate événement et envoyer un événement popstate lorsque vous utilisez history.pushState() devrait suffire à agir sur href change:

window.addEventListener('popstate', listener);

const pushUrl = (href) => {
  history.pushState({}, '', href);
  window.dispatchEvent(new Event('popstate'));
};
60
PHF

Vous ne pouvez pas éviter les scrutations, il n'y a pas d'événement pour changer href.

De toute façon, utiliser des intervalles est assez léger si vous n'allez pas trop loin. Vérifier le href toutes les 50 ms ou plus n'aura aucun effet significatif sur les performances si cela vous inquiète.

21
Tatu Ulmanen

J'utilise ce script dans mon extension "Grab Any Media" et je fonctionne bien (comme youtube case)

var oldHref = document.location.href;

window.onload = function() {

    var
         bodyList = document.querySelector("body")

        ,observer = new MutationObserver(function(mutations) {

            mutations.forEach(function(mutation) {

                if (oldHref != document.location.href) {

                    oldHref = document.location.href;

                    /* Changed ! your code here */

                }

            });

        });

    var config = {
        childList: true,
        subtree: true
    };

    observer.observe(bodyList, config);

};
15
Leonardo Ciaccio

Vous pouvez utiliser un événement onhashchange par défaut.

documenté ICI

Et peut être utilisé comme ceci:

function locationHashChanged( e ) {
    console.log( location.hash );
    console.log( e.oldURL, e.newURL );
    if ( location.hash === "#pageX" ) {
        pageX();
    }
}

window.onhashchange = locationHashChanged;

Si le navigateur ne supporte pas oldURL et newURL, vous pouvez le lier comme ceci:

//let this snippet run before your hashChange event binding code
if( !window.HashChangeEvent )( function() {
    let lastURL = document.URL;
    window.addEventListener( "hashchange", function( event ) {
        Object.defineProperty( event, "oldURL", { enumerable: true, configurable: true, value: lastURL } );
        Object.defineProperty( event, "newURL", { enumerable: true, configurable: true, value: document.URL } );
        lastURL = document.URL;
    } );
} () );
9
iSkore

Avez-vous essayé beforeUnload? Cet événement se déclenche immédiatement avant que la page ne réponde à une demande de navigation, ce qui devrait inclure la modification du href.

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
    <HTML>
    <HEAD>
    <TITLE></TITLE>
    <META NAME="Generator" CONTENT="TextPad 4.6">
    <META NAME="Author" CONTENT="?">
    <META NAME="Keywords" CONTENT="?">
    <META NAME="Description" CONTENT="?">
    </HEAD>

         <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js" type="text/javascript"></script>
            <script type="text/javascript">
            $(document).ready(function(){
                $(window).unload(
                        function(event) {
                            alert("navigating");
                        }
                );
                $("#theButton").click(
                    function(event){
                        alert("Starting navigation");
                        window.location.href = "http://www.bbc.co.uk";
                    }
                );

            });
            </script>


    <BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#FF0000" VLINK="#800000" ALINK="#FF00FF" BACKGROUND="?">

        <button id="theButton">Click to navigate</button>

        <a href="http://www.google.co.uk"> Google</a>
    </BODY>
    </HTML>

Attention, cependant, votre événement sera déclenché ( à chaque fois que vous naviguez en dehors de la page, que ce soit à cause du script, ou quelqu'un qui clique sur un lien. Votre véritable défi consiste à détecter les différentes raisons du déclenchement de l’événement. (Si cela est important pour votre logique)

7
belugabob

Avec Jquery , essayez juste

$(window).on('beforeunload', function () {
    //your code goes here on location change 
});

En utilisant javascript :

window.addEventListener("beforeunload", function (event) {
   //your code goes here on location change 
});

Voir le document: https://developer.mozilla.org/en-US/docs/Web/Events/beforeunload

2

Eh bien, il y a 2 façons de changer le location.href. Soit vous pouvez écrire location.href = "y.html", qui recharge la page ou peut utiliser l’API d’historique qui ne recharge pas la page. J'ai expérimenté le premier beaucoup récemment.

Si vous ouvrez une fenêtre enfant et capturez le chargement de la page enfant à partir de la fenêtre parente, les différents navigateurs se comportent très différemment. La seule chose courante est qu'ils suppriment l'ancien document et en ajoutent un nouveau. Par exemple, l'ajout de readystatechange ou le chargement de gestionnaires d'événements dans l'ancien document n'a aucun effet. La plupart des navigateurs suppriment également les gestionnaires d’événements de l’objet window, la seule exception étant Firefox. Dans Chrome avec Karma runner et dans Firefox, vous pouvez capturer le nouveau document dans le chargement de readyState si vous utilisez unload + next tick. Vous pouvez donc ajouter, par exemple, un gestionnaire d’événements load ou un gestionnaire d’événements readystatechange, ou simplement indiquer que le navigateur charge une page avec un nouvel URI. Dans Chrome avec test manuel (probablement aussi avec GreaseMonkey)) et dans Opera, PhantomJS, IE10, IE11, vous ne pouvez pas capturer le nouveau document à l'état de chargement. Dans ces navigateurs, le unload + next tick appelle le rappel quelques centaines de ms plus tard que l'événement de chargement de la page. Le délai est généralement de 100 à 300 ms, mais opera simetime génère un délai de 750 ms pour le prochain tick, ce qui est effrayant. Si vous voulez un résultat cohérent dans tous les navigateurs, faites ce que vous voulez. vouloir après l'événement de chargement, mais rien ne garantit que l'emplacement ne sera pas remplacé avant.

var uuid = "win." + Math.random();
var timeOrigin = new Date();
var win = window.open("about:blank", uuid, "menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes");


var callBacks = [];
var uglyHax = function (){
    var done = function (){
        uglyHax();
        callBacks.forEach(function (cb){
            cb();
        });
    };
    win.addEventListener("unload", function unloadListener(){
        win.removeEventListener("unload", unloadListener); // Firefox remembers, other browsers don't
        setTimeout(function (){
            // IE10, IE11, Opera, PhantomJS, Chrome has a complete new document at this point
            // Chrome on Karma, Firefox has a loading new document at this point
            win.document.readyState; // IE10 and IE11 sometimes fails if I don't access it twice, idk. how or why
            if (win.document.readyState === "complete")
                done();
            else
                win.addEventListener("load", function (){
                    setTimeout(done, 0);
                });
        }, 0);
    });
};
uglyHax();


callBacks.Push(function (){
    console.log("cb", win.location.href, win.document.readyState);
    if (win.location.href !== "http://localhost:4444/y.html")
        win.location.href = "http://localhost:4444/y.html";
    else
        console.log("done");
});
win.location.href = "http://localhost:4444/x.html";

Si vous exécutez votre script uniquement dans Firefox, vous pouvez utiliser une version simplifiée et capturer le document dans un état de chargement. Ainsi, par exemple, un script de la page chargée ne peut pas quitter sans vous enregistrer la modification de l'URI:

var uuid = "win." + Math.random();
var timeOrigin = new Date();
var win = window.open("about:blank", uuid, "menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes");


var callBacks = [];
win.addEventListener("unload", function unloadListener(){
    setTimeout(function (){
        callBacks.forEach(function (cb){
            cb();
        });
    }, 0);
});


callBacks.Push(function (){
    console.log("cb", win.location.href, win.document.readyState);
    // be aware that the page is in loading readyState, 
    // so if you rewrite the location here, the actual page will be never loaded, just the new one
    if (win.location.href !== "http://localhost:4444/y.html")
        win.location.href = "http://localhost:4444/y.html";
    else
        console.log("done");
});
win.location.href = "http://localhost:4444/x.html";

Si nous parlons d'applications d'une page qui changent la partie de hachage de l'URI ou utilisent l'API d'historique, vous pouvez utiliser respectivement les événements hashchange et popstate de la fenêtre. Ceux-ci peuvent capturer même si vous avancez dans l’historique jusqu’à ce que vous restiez sur la même page. Le document ne change pas par ceux-ci et la page n'est pas vraiment rechargée.

1
inf3rno