JavaScript'te karma işleminden sonra URL'nin değişip değişmediğini belirleme


161

JavaScript'te bir URL'nin değişip değişmediğini nasıl kontrol edebilirim? Örneğin, AJAX kullanan GitHub gibi web siteleri, # simgesinden sonra sayfayı yeniden yüklemeden benzersiz bir URL oluşturmak için sayfa bilgilerini ekler. Bu URL'nin değişip değişmediğini tespit etmenin en iyi yolu nedir?

  • Is onloadolay yine aradı?
  • URL için bir olay işleyici var mı?
  • Veya bir değişikliği algılamak için URL saniyede bir kontrol edilmeli mi?

1
Gelecek ziyaretçiler için, @aljgom'un 2018'den yeni bir cevabı en iyi çözümdür: stackoverflow.com/a/52809105/151503
Redzarf

Yanıtlar:


106

Modern tarayıcılarda (IE8 +, FF3.6 +, Chrome), yalnızca hashchangeetkinliği dinleyebilirsiniz window.

Bazı eski tarayıcılarda, sürekli kontrol eden bir zamanlayıcıya ihtiyacınız vardır location.hash. JQuery kullanıyorsanız , tam olarak bunu yapan bir eklenti vardır.


133
Bu, anladığım kadarıyla, yalnızca # işaretinden sonra parçanın değişmesi için çalışıyor (dolayısıyla etkinlik adı)? Ve tam URL değişikliği için değil, sorunun başlığı tarafından ima edildiği gibi.
NPC

13
@NPC Tam URL değişikliği için herhangi bir işleyici (bağlantı etiketi olmadan)?
Neha Choudhary

Zaman aşımı olaylarına nadiren ihtiyacınız vardır: denetlemek için fare ve tuşboyutlarını kullanın.
Sjeiti

yol değişirse karma olmazsa ne olur?
SuperUberDuper

@SuperUberDuper Kullanıcı bir gezinme başlattığı / bir bağlantıyı vb. Tıklattığı için yol değişirse, yalnızca bir beforeunloadolay görürsünüz . Kodunuz URL değişikliğini başlattıysa, en iyisini bilir.
phihag

92

locationchangeEtkinlik dinleyicileri ekleyebilmek istedim . Aşağıdaki değişiklikten sonra, bunu yapabiliriz, bunun gibi

window.addEventListener('locationchange', function(){
    console.log('location changed!');
})

Buna karşılık, window.addEventListener('hashchange',()=>{})yalnızca bir url'deki bir hashtag'den sonraki bölüm değişirse ve window.addEventListener('popstate',()=>{})her zaman çalışmazsa ateşlenir.

Bu değişiklik, Christian'ın cevabına benzer şekilde , geçmiş nesnesini bazı işlevler ekleyecek şekilde değiştirir.

Varsayılan olarak, bir popstateetkinlik vardır, ancak pushstateve için hiçbir etkinlik yoktur replacestate.

Bu değiştirir bu üç işlev böylece tüm yangın özel bir locationchangede kullanabilir ve için için olay pushstateve replacestateo kullanmak istiyorsanız olaylar:

/* These are the modifications: */
history.pushState = ( f => function pushState(){
    var ret = f.apply(this, arguments);
    window.dispatchEvent(new Event('pushstate'));
    window.dispatchEvent(new Event('locationchange'));
    return ret;
})(history.pushState);

history.replaceState = ( f => function replaceState(){
    var ret = f.apply(this, arguments);
    window.dispatchEvent(new Event('replacestate'));
    window.dispatchEvent(new Event('locationchange'));
    return ret;
})(history.replaceState);

window.addEventListener('popstate',()=>{
    window.dispatchEvent(new Event('locationchange'))
});

2
Harika, ihtiyacım olan şey Teşekkürler
eslamb

Teşekkürler, gerçekten yararlı!
Thomas Lang

1
IE'de bunu yapmanın bir yolu var mı? Desteklemediği için =>
joshuascotton

1
@joshuacotton => bir ok işlevidir, f => fname () {...} işlevini (f) {return function fname () {...}}
aljgom ile

1
Bu çok yararlı ve bir cazibe gibi çalışır. Bu kabul edilen cevap olmalı.
Kunal Parekh

77

bu kodu kullan

window.onhashchange = function() { 
     //code  
}

jQuery ile

$(window).bind('hashchange', function() {
     //code
});

7
Bunun cevabı işaretlenmelidir. Bu bir satır, tarayıcı olay modelini kullanıyor ve sonsuz kaynak tüketen zaman aşımlarına dayanmıyor
Nick Mitchell

1
Bence buradaki en iyi cevap. Eklenti ve minimum kod yok
agDev

15
Slack tarafından uygulanan gibi çok popüler görünen karma olmayan url değişiklikleri üzerinde çalışmaz
NycCompSci

27
Bu sadece url'de bir karma olduğunda tetiklenirse en iyi cevap nasıl olur?
Aaron

1
Başka bir şeyin de dinlemek istemesi durumunda, onhashchange değerini doğrudan değiştirmek yerine addEventListener kullanmalısınız.
broken-e

55

Biraz araştırma yaptıktan sonra DÜZENLE:

Her nasılsa Mozilla belgelerinde bulunan belgelerden dolayı kandırıldığım anlaşılıyor. popstateOlay (ve geri arama işlevi onpopstate) olan tetiklenmez zaman pushState()veya replaceState()kod denir. Bu nedenle orijinal cevap her durumda geçerli değildir.

Bununla birlikte, @ alpha123'e göre işlevleri maymunla yamalayarak bunu atlatmanın bir yolu vardır :

var pushState = history.pushState;
history.pushState = function () {
    pushState.apply(history, arguments);
    fireEvents('pushState', arguments);  // Some event-handling function
};

Orijinal cevap

Bu sorunun başlığının " URL değişikliği nasıl algılanır " olduğu göz önüne alındığında , tam yolun ne zaman değiştiğini (ve yalnızca karma çapayı değil) bilmek istediğinizde, yanıtı dinleyebilirsiniz popstate:

window.onpopstate = function(event) {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
};

Mozilla Docs içindeki popstate referansı

Şu anda (Ocak 2017) dünya çapında tarayıcıların % 92'sinden popstate desteği var .


11
İnanılmaz bir şekilde 2018'de hala bu tür hack'lere başvurmalıyız.
Keçi

1
Bu benim kullanım durumum için çalıştı - ama @goat'ın dediği gibi - bunun için yerel bir destek olmaması inanılmaz ...
wasddd_

1
hangi argümanlar? fireEvents'ı nasıl kurarım?
SeanMC

2
Bunun birçok durumda da güvenilir olmadığını unutmayın. Örneğin, farklı Amazon ürün varyasyonlarını (fiyatın altındaki kutucuklar) tıkladığınızda URL değişikliğini algılamaz.
thdoan

2
bu, location.back () kullanılmıyorsa localhost / foo'dan localhost / baa'ya bir değişiklik algılamayacak
SuperUberDuper

53

Jquery (ve bir eklenti) ile şunları yapabilirsiniz

$(window).bind('hashchange', function() {
 /* things */
});

http://benalman.com/projects/jquery-hashchange-plugin/

Aksi takdirde evet, setInterval'ı kullanmanız ve hash olayında (window.location.hash) bir değişiklik olup olmadığını kontrol etmeniz gerekir.

Güncelleme! Basit bir taslak

function hashHandler(){
    this.oldHash = window.location.hash;
    this.Check;

    var that = this;
    var detect = function(){
        if(that.oldHash!=window.location.hash){
            alert("HASH CHANGED - new has" + window.location.hash);
            that.oldHash = window.location.hash;
        }
    };
    this.Check = setInterval(function(){ detect() }, 100);
}

var hashDetection = new hashHandler();

(window.location) değişikliğini algılayıp işleyebilir miyim? (jquery olmadan)
BergP

7
@BergP, Düz javascript dinleyicisini kullanarak: window.addEventListener("hashchange", hashChanged);
Ron

1
Bu kadar kısa zaman aralığı uygulama için iyi mi? Yani, tarayıcı detect()işlevi yerine getirmek için çok meşgul değil mi?
Hasib Mahmud

4
@HasibMahmud, bu kod her 100 ms'de bir eşitlik kontrolü yapıyor. Tarayıcımda 1 ms'nin altında 500 eşitlik kontrolü yapabileceğimi kıyasladım. Bu kod, işlem gücümün 1 / 50000'ünü kullanıyor. Çok fazla endişelenmezdim.
z5h

24

Bir karma değişiklik olay dinleyicisi ekleyin!

window.addEventListener('hashchange', function(e){console.log('hash changed')});

Veya tüm URL değişikliklerini dinlemek için:

window.addEventListener('popstate', function(e){console.log('url changed')});

Bu, aşağıdaki kod gibi bir şeyden daha iyidir çünkü window.onhashchange'te yalnızca bir şey var olabilir ve muhtemelen başka birinin kodunun üzerine yazacaksınız.

// Bad code example

window.onhashchange = function() { 
     // Code that overwrites whatever was previously in window.onhashchange  
}

1
pop devlet sadece bir devlet pop, bir itin değil
tetiklenir

8

bu çözüm benim için çalıştı:

var oldURL = "";
var currentURL = window.location.href;
function checkURLchange(currentURL){
    if(currentURL != oldURL){
        alert("url changed!");
        oldURL = currentURL;
    }

    oldURL = window.location.href;
    setInterval(function() {
        checkURLchange(window.location.href);
    }, 1000);
}

checkURLchange();

19
Bu oldukça basit bir yöntem, bence daha yükseğe nişan alabiliriz.
Carles Alcolea

8
@CarlesAlcolea ile bunun eski olduğunu kabul etsem de, tecrübelerime göre hala tüm url değişikliklerinin% 100'ünü yakalamanın tek yolu bu.
Trev14

5
@ahofmann değişen (bir yorum olmalıydı bir düzenleme olarak) göstermektedir setIntervaliçin setTimeout:. "Bu checkURLchange () her saniyeye yeni bir arama yaratacak, çünkü bir süre sonra durma noktasına Tarayıcı getirecek setInterval () kullanarak setTimeout () doğru çözümdür, çünkü sadece bir kez çağrılır. "
divibisan

Bu, tüm URL değişikliklerini yakalamak için tek çözüm olmuştur, ancak kaynak tüketimi beni başka çözümler aramaya zorluyor.
ReturnTable

3
Veya setTimeoutlike @divibisan önermek yerine setInterval, fonksiyonun dışını hareket ettirin . checkURLchange();ayrıca isteğe bağlı hale gelir.
19:39

4

Eski bir soru olmasına rağmen, Yer çubuğu projesi çok faydalıdır.

var LocationBar = require("location-bar");
var locationBar = new LocationBar();

// listen to all changes to the location bar
locationBar.onChange(function (path) {
  console.log("the current url is", path);
});

// listen to a specific change to location bar
// e.g. Backbone builds on top of this method to implement
// it's simple parametrized Backbone.Router
locationBar.route(/some\-regex/, function () {
  // only called when the current url matches the regex
});

locationBar.start({
  pushState: true
});

// update the address bar and add a new entry in browsers history
locationBar.update("/some/url?param=123");

// update the address bar but don't add the entry in history
locationBar.update("/some/url", {replace: true});

// update the address bar and call the `change` callback
locationBar.update("/some/url", {trigger: true});

Bu nodeJ'ler için, istemci tarafında kullanmak için browserify kullanmamız gerekiyor. Öyle değil mi?
hack4mer

1
Hayır değil. Tarayıcıda çalışır
Ray Booysen

3

URL değişikliklerini dinlemek için aşağıya bakın:

window.onpopstate = function(event) {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
};

Belirli bir durumdan sonra dinleyiciyi durdurmak / kaldırmak istiyorsanız bu stili kullanın.

window.addEventListener('popstate', function(e) {
   console.log('url changed')
});

2

Küçük bir krom uzantısı yaparken, bir ek sorun ile aynı sorunu yaşadım: Bazen, sayfa değişikliği ancak URL değil.

Örneğin, Facebook Ana Sayfasına gidin ve 'Ana Sayfa' düğmesine tıklayın. Sayfayı yeniden yükleyeceksiniz ancak URL değişmeyecek (bir sayfalık uygulama stili).

Zamanın% 99'u, bu etkinlikleri Angular, React, Vue vb.Gibi Çerçevelerden alabilmemiz için web siteleri geliştiriyoruz.

AMA , benim bir Chrome uzantısı (Vanilla JS'de) durumunda, genellikle URL değişti tarafından yakalanabilecek her "sayfa değişikliği" için tetikleyecek bir olay dinlemek zorunda kaldım, ancak bazen değişmez.

Ev yapımı çözümüm şuydu:

listen(window.history.length);
var oldLength = -1;
function listen(currentLength) {
  if (currentLength != oldLength) {
    // Do your stuff here
  }

  oldLength = window.history.length;
  setTimeout(function () {
    listen(window.history.length);
  }, 1000);
}

Temelde pencere geçmişine uygulanan leoneckert çözümü, bir sayfa tek bir sayfa uygulamasında değiştiğinde değişecektir.

Roket bilimi değil, bulduğum en temiz çözüm, burada sadece tamsayı bir eşitliği kontrol ettiğimizi ve daha büyük nesneleri veya tüm DOM'yu değil.


Çözüm basit ve krom uzantılar için çok iyi çalışıyor. Uzunluk yerine YouTube video kimliğini kullanmanızı öneririm. stackoverflow.com/a/3452617/808901
Rod Lima

2
setInterval'ı kullanmamalısınız, çünkü listen'i her aradığınızda (xy) yeni bir Aralık oluşturulur ve binlerce aralıkla sonuçlanırsınız.
Stef Chäser

1
Haklısın, sonra fark ettim ve yazımı değiştirmedim, bunu düzenleyeceğim. RAM sızıntıları nedeniyle Google Chrome'un çökmesiyle bile karşılaştım. Yorumunuz için teşekkür ederiz
Alburkerk

window.historymaksimum 50 uzunluğa sahiptir (en azından Chrome 80'den itibaren). Bu noktadan sonra, window.history.lengthher zaman 50 değerini döndürür. Bu durumda, bu yöntem herhangi bir değişikliği tanıyamaz.
Collin Krawll

1

JQuery unload işlevine bakın. Her şeyi halleder.

https://api.jquery.com/unload/

Unload olayı, kullanıcı sayfadan ayrıldığında pencere öğesine gönderilir. Bu birçok şeyden biri anlamına gelebilir. Kullanıcı sayfadan ayrılmak için bir bağlantıyı tıklayabilir veya adres çubuğuna yeni bir URL yazmış olabilir. İleri ve geri düğmeleri etkinliği tetikler. Tarayıcı penceresinin kapatılması olayın tetiklenmesine neden olur. Bir sayfa yeniden yüklemesi bile önce bir unload olayı oluşturur.

$(window).unload(
    function(event) {
        alert("navigating");
    }
);

2
İçerik Ajaxed olduğunda, URL, pencere kaldırılmadan değişebilir. Bu komut dosyası bir URL değişikliğini algılamaz, ancak yine de her URL değişikliğinde pencere kaldırma işlemi yapan bazı kullanıcılar için yararlı olabilir.
Deborah

@ranbuch sorusuyla aynı, bu yalnızca tek sayfa uygulaması olmayan sayfalar için geçerlidir ve bu etkinlik, URL değişikliğini değil, yalnızca boşaltma penceresi olayını izler.
ncubica

0
window.addEventListener("beforeunload", function (e) {
    // do something
}, false);

11
unload olayı hiçbir zaman tetiklenmeyeceğinden bu tek sayfa uygulamaları bağlamında çalışmaz
ncubica

0

Aşağıdaki cevap buradan gelir (eski javascript sözdizimi ile (ok işlevi yok, IE 10+ desteği)): https://stackoverflow.com/a/52809105/9168962

(function() {
  if (typeof window.CustomEvent === "function") return false; // If not IE
  function CustomEvent(event, params) {
    params = params || {bubbles: false, cancelable: false, detail: null};
    var evt = document.createEvent("CustomEvent");
    evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
    return evt;
  }
  window.CustomEvent = CustomEvent;
})();

(function() {
  history.pushState = function (f) {
    return function pushState() {
      var ret = f.apply(this, arguments);
      window.dispatchEvent(new CustomEvent("pushState"));
      window.dispatchEvent(new CustomEvent("locationchange"));
      return ret;
    };
  }(history.pushState);
  history.replaceState = function (f) {
    return function replaceState() {
      var ret = f.apply(this, arguments);
      window.dispatchEvent(new CustomEvent("replaceState"));
      window.dispatchEvent(new CustomEvent("locationchange"));
      return ret;
    };
  }(history.replaceState);
  window.addEventListener("popstate", function() {
    window.dispatchEvent(new CustomEvent("locationchange"));
  });
})();

0

Bunu yapmanın bir başka basit yolu, tıklandığında ne zaman olduğunu tespit etmek için sayfadaki bağlantı etiketlerine bir sınıf adı aracılığıyla bir click olayı eklemektir. Artık url verilerini almak için window.location.href komutunu kullanabilirsiniz. ajax isteğinizi sunucuya çalıştırmak için kullanabilirsiniz. Basit ve kolay.


-1

setIntervalHer çağrıda bir öncekini iptal etmeden yeni bir işe başlıyorsunuz - muhtemelen sadece birsetTimeout

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.