关于javascript:window.location.href更改时的事件

Event when window.location.href changes

我正在为某个网站编写Greasemonkey脚本,该脚本有时会修改location.href

当页面上的window.location.href更改时,如何获取事件(通过window.addEventListener或类似的东西)? 我还需要访问指向新的/修改的URL的文档的DOM。

我已经看到了其他涉及超时和轮询的解决方案,但如果可能的话,我想避免这种情况。


popstate事件:

The popstate event is fired when the active history entry changes. [...] The popstate event is only triggered by doing a browser action such as a click on the back button (or calling history.back() in JavaScript)

因此,在使用history.pushState()时侦听popstate事件并发送popstate事件应该足以对href更改采取措施:

1
2
3
4
5
6
window.addEventListener('popstate', listener);

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


我在扩展程序" Grab Any Media"中使用了此脚本,并且工作正常(如youtube case)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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);

};


您无法避免轮询,没有任何事件可更改href。

无论如何,只要不过度使用间隔,就很轻松。如果您担心的话,每隔50毫秒左右检查一次href对性能不会有任何重大影响。


您可以使用默认的onhashchange事件。

此处记录

可以这样使用:

1
2
3
4
5
6
7
8
9
function locationHashChanged( e ) {
    console.log( location.hash );
    console.log( e.oldURL, e.newURL );
    if ( location.hash ==="#pageX" ) {
        pageX();
    }
}

window.onhashchange = locationHashChanged;

如果浏览器不支持oldURLnewURL,则可以这样绑定它:

1
2
3
4
5
6
7
8
9
//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;
    } );
} () );


您是否尝试过beforeUnload?
该事件在页面响应导航请求之前立即触发,并且其中应包括对href的修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    <!DOCTYPE HTML PUBLIC"-//W3C//DTD HTML 3.2 Final//EN">
    <HTML>
    <HEAD>
   
    <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 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";
                    }
                );

            });
           


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

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

         Google
    </BODY>
    </HTML>

但是请注意,无论您是离开脚本还是某个人单击链接,只要您离开页面导航,就会触发您的事件。
您真正的挑战是检测引发事件的不同原因。 (如果这对您的逻辑很重要)


通过Jquery,只需尝试

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

通过使用javascript:

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

请参阅文档:https://developer.mozilla.org/en-US/docs/Web/Events/beforeunload


那么,有两种方法可以更改location.href。您可以编写location.href="y.html"来重新加载页面,也可以使用历史API来重新加载页面。我最近尝试了很多。

如果打开子窗口并从父窗口捕获子页面的负载,则不同的浏览器的行为会大不相同。唯一常见的是,它们删除了旧文档并添加了一个新文档,因此,例如,将readystatechange或load事件处理程序添加到旧文档中没有任何作用。大多数浏览器也会从window对象中删除事件处理程序,唯一的例外是Firefox。如果使用unload + next tick,则在带有KarmaRunner的Chrome和Firefox中,可以在加载readyState中捕获新文档。因此,您可以添加例如加载事件处理程序或readystatechange事件处理程序,或者仅记录浏览器正在使用新URI加载页面。在具有手动测试功能的Chrome(也可能是GreaseMonkey)中,以及在Opera,PhantomJS,IE10,IE11中,您无法在加载状态下捕获新文档。在这些浏览器中,unload + next tick在页面的加载事件发生后几百毫秒后调用回调。延迟通常为100到300毫秒,但是歌剧simetime对于下一个滴答声会产生750毫秒的延迟,这很可怕。因此,如果要在所有浏览器中获得一致的结果,则可以在load事件之后执行所需的操作,但是不能保证在此之前不会覆盖该位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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";

如果仅在Firefox中运行脚本,则可以使用简化版本并以加载状态捕获文档,例如,在记录URI更改之前,已加载页面上的脚本无法导航:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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";

如果我们正在谈论更改URI的哈希部分或使用历史记录API的单页应用程序,则可以分别使用窗口的hashchangepopstate事件。即使您在历史记录中来回移动,直到您停留在同一页面上,它们也可以捕获。那些文档不会更改,页面也不会真正重新加载。