Window.location.href değiştiğinde olay


88

Bir noktada değişen bir site için Greasemonkey komut dosyası yazıyorum location.href.

Bir sayfada değişiklik window.addEventListenerolduğunda nasıl bir etkinlik (aracılığıyla veya benzer bir şey) edinebilirim window.location.href? Ayrıca yeni / değiştirilmiş url'yi gösteren dokümanın DOM'suna da erişmem gerekiyor.

Zaman aşımları ve yoklama içeren başka çözümler de gördüm, ancak mümkünse bundan kaçınmak istiyorum.


YouTube için: javascript -
user202729

Yanıtlar:


90

popstate olayı :

Popstate olayı, etkin geçmiş girişi değiştiğinde tetiklenir. [...] popstate olayı yalnızca geri düğmesine tıklama (veya JavaScript'te history.back () işlevini çağırma) gibi bir tarayıcı eylemi gerçekleştirilerek tetiklenir

Bu nedenle, popstateolayları dinlemek ve popstatekullanırken bir olay göndermek history.pushState(), hrefdeğişiklik konusunda harekete geçmek için yeterli olmalıdır :

window.addEventListener('popstate', listener);

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

2
Ama popstate içerik yüklenmeden önce ateş edecek mi?
user2782001

4
kod parçası üzerinde herhangi bir kontrolü yoksa kullanılamazpushState
Nathan Gouy

2
Bu her zaman işe yaramaz, ateşlenecek bir popstate olayına dayanır. Örneğin, youtube içinde gezinmek tetiklenmez, yalnızca geçmişte geri veya ileri doğru bastığınızda
TrySpace

57

Bu komut dosyasını "Grab Any Media" uzantımda kullanıyorum ve sorunsuz çalışıyorum ( youtube vakası gibi )

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);

};

Bir şekilde, bu yaklaşım gibi ... :) biraz RxJs tarzı bir hissediyor
GUNTRAM

Youtube için işe yarayabilecek tek çözüm: [Yalnızca Rapor] Aşağıdaki İçerik Güvenlik Politikası yönergesini ihlal ettiği için ' youtube.com/sw.js'den bir çalışan oluşturmayı reddetti : "worker-src' none".
Simon Meusel

MutationObserver

1
Kullanım durumum için hiçbir sorun olmadan kutudan çıktı, Tanrı korusun! Bunun için henüz yerel bir olay olmaması can sıkıcı (popstate benim için işe yaramadı) ama bir gün! window.addEventListener("load", () => {})Window.onload yerine kullanırdım gerçi :)
Sanchit Batra

bu çalışır, uzantı bağlamında bile history.pushState'i algılar
YangombiUmpakati

29

Yoklamayı önleyemezsiniz, href değişikliği için herhangi bir olay yoktur.

Denize girmezseniz, aralıkları kullanmak oldukça hafiftir. Href'i her 50 ms'de bir kontrol etmek, bundan endişeleniyorsanız performans üzerinde önemli bir etkiye sahip olmayacaktır.


3
@AlexanderMills: Neyse ki bu yeni çözümler var popstateve hashchange.
serv-inc

1
Bu doğru değil.
inf3rno

@ MartinZvarík 2010'da bile, yeni belgenin yüklenmesi için boşaltma olayının yanı sıra bir mikro veya makro görevi kullanmak mümkündü.
inf3rno

3
Ne yazık ki bu cevap 2020'de hala doğrudur. Popstate yalnızca kullanıcı ileri / geri düğmelerini kullandığında ve hashchange yalnızca hash değişiklikleri için tetiklendiğinde tetiklenir. Konumun değişmesine neden olan kod üzerinde kontrolünüz yoksa, 🤷‍♀️
Rico Kahler

14

Kullanabileceğiniz varsayılan bir onhashchangeolay var.

BURADA belgelenmiştir

Ve şu şekilde kullanılabilir:

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

window.onhashchange = locationHashChanged;

Tarayıcı desteklemiyorsa oldURLve newURLbunu şu şekilde bağlayabilirsiniz:

//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;
    } );
} () );

8
Hashchange olayları, yalnızca URL'nizin normalde bağlantı bağlantıları için kullanılan '#' sembolüyle başlayan bir bölümü varsa gönderilir. Aksi takdirde ateşlenmez.
Fredrik_Macrobond

1
window.addEventListener ("hashchange", funcRef, false);
Bishoy Hanna

7

Daha önce denediniz mi? Bu olay, sayfa bir gezinme isteğine yanıt vermeden hemen önce tetiklenir ve bu, href'in değiştirilmesini içermelidir.

    <!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>

Etkinlik ateş edeceğini, ancak, dikkat zaman bu çünkü bir bağlantı komut dosyası ya da biri tıklayarak arasında olup olmadığı, sayfadan ayrılmayın. Asıl zorluğunuz, tetiklenen olayın farklı nedenlerini tespit etmektir. (Bu mantığınız için önemliyse)


Bunun işe yarayıp yaramayacağından emin değilim çünkü çoğu zaman karma değişikliklerde sayfanın yeniden yüklenmesi gerekmez.
samandmoore

Yeni DOM'a da erişmem gerekiyor, bunu netleştirmek için soruyu güncelledim.
Johan Dahlin

Tamam - hashdurumu dikkate almadım - bunu düşüneceğim. Yeni DOM'a erişebilmeye gelince, olay yeni DOM yüklenmeden önce tetikleneceği için şansınız kalmaz (buna bir olay işleyicisinden erişim söz konusu olduğunda). Mantığı yeni sayfanın onload olayına dahil edebilirsiniz, ancak o sayfanın her yüklemesi için mantığı uygulamanız gerekip gerekmediğini belirleme konusunda aynı sorunlara sahip olabilirsiniz. Sayfa akışı dahil, başarmaya çalıştığınız şey hakkında biraz daha ayrıntı verebilir misiniz?
belugabob

Onbeforeunload olay işleyicisinin içindeki location.href'e erişmeyi denedim ve hedef url'yi değil orijinal url'yi gösteriyor.
CMCDragonkai

Unloading, sayfanın boşaltılmasını iptal edebilir, böylece navigasyonu iptal etmek istemiyorsanız unload olayı daha iyidir.
inf3rno


2

.Txt dosyasını değiştirmenin 2 yolu vardır location.href. Ya location.href = "y.html"sayfayı yeniden yükleyen yazabilir ya da sayfayı yeniden yüklemeyen geçmiş API'sini kullanabilirsiniz. Son zamanlarda ilkini çok denedim.

Bir alt pencere açarsanız ve alt sayfanın yükünü ana pencereden yakalarsanız, farklı tarayıcılar çok farklı davranır. Ortak olan tek şey, eski belgeyi kaldırmaları ve yenisini eklemeleridir, bu nedenle örneğin eski belgeye readystatechange veya load olay işleyicileri eklemenin herhangi bir etkisi yoktur. Tarayıcıların çoğu olay işleyicilerini pencere nesnesinden de kaldırır, tek istisna Firefox'tur. Karma runner ile Chrome'da ve Firefox'ta, kullanıyorsanız yeni belgeyi yükleniyor readyState'te yakalayabilirsiniz.unload + next tick. Böylece, örneğin bir yükleme olayı işleyicisi veya bir okuma değiştirme olay işleyicisi ekleyebilir veya tarayıcının yeni bir URI ile bir sayfayı yüklediğini günlüğe kaydedebilirsiniz. Manuel test içeren Chrome'da (muhtemelen GreaseMonkey de) ve Opera, PhantomJS, IE10, IE11'de yeni belgeyi yükleme durumunda yakalayamazsınız. Bu tarayıcılarda, unload + next tickgeri aramayı, sayfanın tetiklenme olayından birkaç yüz milisaniye sonra çağırır. Gecikme tipik olarak 100 ila 300 milisaniyedir, ancak opera simetime süresi, korkutucu olan bir sonraki tıklama için 750 milisaniye gecikme yapar. Dolayısıyla, tüm tarayıcılarda tutarlı bir sonuç istiyorsanız, yükleme olayından sonra istediğinizi yaparsınız, ancak bundan önce konumun geçersiz kılınmayacağına dair bir garanti yoktur.

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";

Komut dosyanızı yalnızca Firefox'ta çalıştırırsanız, basitleştirilmiş bir sürüm kullanabilir ve belgeyi bir yükleme durumunda yakalayabilirsiniz, böylece örneğin, yüklenen sayfadaki bir komut dosyası siz URI değişikliğini günlüğe kaydetmeden önce başka yere gidemez:

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'nin hash kısmını değiştiren veya geçmiş API'sini kullanan tek sayfalı uygulamalardan bahsediyorsak, sırasıyla pencerenin hashchangeve popstateolaylarını kullanabilirsiniz . Aynı sayfada kalana kadar geçmişte ileri geri hareket etseniz bile bunlar yakalayabilir. Belge bunlar tarafından değiştirilmez ve sayfa gerçekten yeniden yüklenmez.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.