Sayfanın Chrome'da yüklendiğinde popstate


94

Web uygulamam için History API kullanıyorum ve bir sorunla karşılaşıyorum. Sayfadaki bazı sonuçları güncellemek için Ajax çağrıları yapıyorum ve tarayıcının konum çubuğunu sayfayı yeniden yüklemeden güncellemek için history.pushState () kullanıyorum . Daha sonra, tabii ki, geri düğmesi tıklandığında önceki durumu geri yüklemek için window.popstate kullanıyorum .

Sorun iyi biliniyor - Chrome ve Firefox bu popstate olayını farklı şekilde ele alıyor. Firefox ilk yüklemede onu çalıştırmazken, Chrome bunu yapıyor. Firefox tarzı olmasını istiyorum ve olayı yüklerken tetiklememek istiyorum, çünkü sonuçları yüklerken tamamen aynı olanlarla güncelliyor. History.js kullanmanın dışında bir çözüm var mı ? Kullanmak istemememin nedeni - kendi başına çok fazla JS kitaplığına ihtiyaç duyması ve zaten çok fazla JS içeren bir CMS'de uygulanmasına ihtiyacım olduğundan, JS'yi en aza indirmek istiyorum. .

Bu nedenle, Chrome'un yükleme sırasında 'popstate'i çalıştırmamasını sağlamanın bir yolu olup olmadığını veya belki de birisinin tüm kitaplıklar tek bir dosyada birleştirildiği için History.js'yi kullanmaya çalışıp çalışmadığını bilmek isteriz.


3
Eklemek gerekirse, Firefox'un davranışı belirlenmiş ve doğru davranıştır. Chrome 34 itibariyle, artık düzeltildi! Opera geliştiricileri için Yaşasın!
Henry

Yanıtlar:


49

Sürüm 19'daki Google Chrome'da @ spliter'ın çözümü çalışmayı durdurdu. @Johnnymire'ın da belirttiği gibi, Chrome 19'da history.state var, ancak boş.

Çözümüm , window.history'de durum olup olmadığını kontrol etmek için window.history.state! == null eklemektir :

var popped = ('state' in window.history && window.history.state !== null), initialURL = location.href;

Bunu tüm büyük tarayıcılarda ve Chrome 19 ve 18 sürümlerinde test ettim. Çalışıyor gibi görünüyor.


5
Söylediğin için teşekkürler; Sana basitleştirmek düşünüyorum inve nullsadece kontrol != nullo ilgilenir beri undefinedde.
pimvdb

5
Hep kullanıyorsanız bu irade sadece işi kullanma nullsenin içinde history.pushState()gibi yöntemlerle history.pushState(null,null,'http//example.com/). Kabul edildi, bu muhtemelen çoğu kullanım olurdu (jquery.pjax.js ve diğer demoların çoğunda böyle kurulur). Ancak tarayıcılar window.history.state(FF ve Chrome 19+ gibi) uygularsa , window.history.state bir yenilemede veya tarayıcı yeniden başlatıldıktan sonra boş olmayabilir
açıklanmayanBacn

19
Bu artık Chrome 21'de benim için çalışmıyor. Sayfa yüklenirken yalnızca yanlış olarak ayarlandım ve ardından herhangi bir itme veya açılma durumunda atlamayı doğru olarak ayarladım. Bu, Chrome'un ilk yüklemede patlamasını etkisiz hale getirir. Burada biraz daha iyi açıklanmıştır: github.com/defunkt/jquery-pjax/issues/143#issuecomment-6194330
Chad von Nau

1
@ChadvonNau mükemmel bir fikir ve bir zevk - çok teşekkürler!
sowasred2012

Aynı problemle geldim ve basit @ChadvonNau çözümünü kullanmayı bitirdim.
Pherrymason

30

Onpopstate'e eklediğiniz her bir işleyici için özel önlemler almak istemiyorsanız, çözümüm sizin için ilginç olabilir. Bu çözümün büyük bir artısı da onpopstate olaylarının sayfa yüklemesi tamamlanmadan önce ele alınabilmesidir.

Herhangi bir onpopstate işleyicisi eklemeden önce bu kodu bir kez çalıştırın ve her şey beklendiği gibi çalışmalıdır (diğer adıyla Mozilla ^^) .

(function() {
    // There's nothing to do for older browsers ;)
    if (!window.addEventListener)
        return;
    var blockPopstateEvent = document.readyState!="complete";
    window.addEventListener("load", function() {
        // The timeout ensures that popstate-events will be unblocked right
        // after the load event occured, but not in the same event-loop cycle.
        setTimeout(function(){ blockPopstateEvent = false; }, 0);
    }, false);
    window.addEventListener("popstate", function(evt) {
        if (blockPopstateEvent && document.readyState=="complete") {
            evt.preventDefault();
            evt.stopImmediatePropagation();
        }
    }, false);
})();

Nasıl çalışır:

Chrome , Safari ve muhtemelen diğer webkit tarayıcıları, belge yüklendiğinde onpopstate olayını tetikler. Bu amaçlanmamıştır, bu nedenle, belge yüklendikten sonraki ilk olay döngü döngüsüne kadar popstate olaylarını engelleriz. Bu, preventDefault ve stopImmediatePropagation çağrılarıyla yapılır (stopPropagation stopImmediatePropagation'ın aksine, tüm olay işleyici çağrılarını anında durdurur).

Ancak, Chrome yanlışlıkla patladığında, belgenin readyState'i zaten "tamamlandı" olduğundan, belge yüklenmeden önce onpopstate çağrılarına izin vermek için belge yükleme tamamlanmadan önce tetiklenen opopstate olaylarına izin veriyoruz.

Güncelleme 2014-04-23: Sayfa yüklendikten sonra komut dosyası çalıştırılırsa popstate olaylarının engellenmesine neden olan bir hata düzeltildi.


4
Benim için şimdiye kadarki en iyi çözüm. Yıllardır setTimeout yöntemini kullanıyorum, ancak sayfa yavaş yüklendiğinde / kullanıcı pushstate'i çok hızlı tetiklediğinde hatalı davranışlara neden oluyor. window.history.statebir durum ayarlanmışsa yeniden yüklemede başarısız olur. PushState kod yığınlarından önce değişken ayarlanıyor. Çözümünüz zariftir ve kod tabanının geri kalanını etkilemeden diğer komut dosyalarının üzerine bırakılabilir. Teşekkürler.
kik

4
Çözüm bu! Keşke bunu günler önce bu sorunu çözmeye çalışırken okursam. Kusursuz çalışan tek kişi budur. Teşekkür ederim!
Ben Fleming

1
Mükemmel açıklama ve denemeye değer tek çözüm.
Sunny R Gupta

Bu harika bir çözüm. Yazma sırasında, en son Chrome ve FF cezası, sorun safari (mac ve iphone) ile ilgilidir. Bu çözüm harika çalışıyor.
Vin

1
Vay be! Safari gününüzü mahvediyorsa, aradığınız kodlar bunlar!
DanO

28

Sadece setTimeout kullanmak doğru bir çözüm değildir çünkü içeriğin yüklenmesinin ne kadar süreceği hakkında hiçbir fikriniz yoktur, bu nedenle zaman aşımından sonra popstate olayının yayınlanması mümkündür.

İşte benim çözümüm: https://gist.github.com/3551566

/*
* Necessary hack because WebKit fires a popstate event on document load
* https://code.google.com/p/chromium/issues/detail?id=63040
* https://bugs.webkit.org/process_bug.cgi
*/
window.addEventListener('load', function() {
  setTimeout(function() {
    window.addEventListener('popstate', function() {
      ...
    });
  }, 0);
});

Benim için işe yarayan tek çözüm bu. Teşekkürler!
Justin

3
tatlı! En çok oylanan ve / veya kabul edilen cevaptan daha iyi sonuç verir!
ProblemsOfSumit

Bu çözüme aktif bağlantısı olan var mı, verilen bağlantıya ulaşamıyorum gist.github.com/3551566
Harbir

1
neden özüne ihtiyacın var?
Oleg Vaskevich

Mükemmel cevap. Teşekkürler.
sideroxylon

25

Çözüm, jquery.pjax.js 195-225 satırlarında bulundu:

// Used to detect initial (useless) popstate.
// If history.state exists, assume browser isn't going to fire initial popstate.
var popped = ('state' in window.history), initialURL = location.href


// popstate handler takes care of the back and forward buttons
//
// You probably shouldn't use pjax on pages with other pushState
// stuff yet.
$(window).bind('popstate', function(event){
  // Ignore inital popstate that some browsers fire on page load
  var initialPop = !popped && location.href == initialURL
  popped = true
  if ( initialPop ) return

  var state = event.state

  if ( state && state.pjax ) {
    var container = state.pjax
    if ( $(container+'').length )
      $.pjax({
        url: state.url || location.href,
        fragment: state.fragment,
        container: container,
        push: false,
        timeout: state.timeout
      })
    else
      window.location = location.href
  }
})

8
Bu çözüm benim için Windows'ta (Chrome 19) çalışmayı bıraktı, hala Mac'te çalışıyor (Chrome 18). History.state Chrome 19'da var ama 18'de yok gibi görünüyor
johnnymire

1
@johnnymire Chrome 19 için çözümümü aşağıda yayınladım: stackoverflow.com/a/10651028/291500
Pavel Linkesch

Chrome 19 sonrası bu davranışı yansıtmak için birinin bu yanıtı düzenlemesi gerekir.
Jorge Suárez de Lis

5
Bir çözüm arayanlar için Torben'in cevabı bugün itibariyle mevcut Chrome ve Firefox sürümlerinde bir cazibe gibi çalışıyor
Jorge Suárez de Lis

1
Şimdi Nisan 2015'te, Safari ile ilgili bir sorunu çözmek için bu çözümü kullandım. Cordova kullanan hibrit bir iOS / Android uygulamasında geri düğmesine basarak bir idare elde etmek için onpopstate etkinliğini kullanmak zorunda kaldım. Safari, programlı olarak adlandırdığım bir yeniden yüklemeden sonra bile onpopstate'i çalıştırıyor. Kodumun kurulum şekliyle, reload () çağrıldıktan sonra sonsuz bir döngüye girdi. Bu onu düzeltti. Sadece olay bildiriminden sonraki ilk 3 satıra ihtiyacım vardı (artı üstteki atamalar).
Martavis P.

14

Pjax'ı yeniden uygulamadan daha doğrudan bir çözüm, pushState'te bir değişken ayarlamak ve değişkeni popState üzerinde kontrol etmektir, böylece ilk popState, yükte tutarsız bir şekilde çalışmaz (jquery'ye özgü bir çözüm değil, yalnızca olaylar için kullanarak):

$(window).bind('popstate', function (ev){
  if (!window.history.ready && !ev.originalEvent.state)
    return; // workaround for popstate on load
});

// ... later ...

function doNavigation(nextPageId) {
  window.history.ready = true;

  history.pushState(state, null, 'content.php?id='+ nextPageId); 
  // ajax in content instead of loading server-side
}

Her zaman yanlış olarak değerlendirilseydi, değil mi? Demek istediğim, window.history.readyher zaman doğrudur.
Andriy Drozdyuk

Örnek, biraz daha net olacak şekilde güncellendi. Temel olarak, tamamen net bir şekilde verdiğinizde yalnızca popstate olaylarını ele almak istersiniz. Aynı etkiyi yüklemeden 500ms sonra bir setTimeout ile elde edebilirsiniz, ancak dürüst olmak gerekirse biraz ucuz.
Tom McKenzie

3
Bu cevap IMO olmalı, Pavels çözümünü denedim ve Firefox'ta düzgün çalışmadı
Porco

Bu benim için bu can sıkıcı soruna kolay bir çözüm olarak çalıştı. Çok teşekkürler!
nirazul

!window.history.ready && !ev.originalEvent.state- Sayfayı yenilediğimde bu iki şey asla tanımlanmıyor. bu nedenle hiçbir kod çalıştırılmaz.
chovy

4

Webkit'in ilk onpopstate olayına atanmış bir durum yoktur, bu nedenle istenmeyen davranışı kontrol etmek için bunu kullanabilirsiniz:

window.onpopstate = function(e){
    if(e.state)
        //do something
};

Orijinal sayfaya geri dönmeye izin veren kapsamlı bir çözüm, bu fikir üzerine inşa edilecektir:

<body onload="init()">
    <a href="page1" onclick="doClick(this); return false;">page 1</a>
    <a href="page2" onclick="doClick(this); return false;">page 2</a>
    <div id="content"></div>
</body>

<script>
function init(){
   openURL(window.location.href);
}
function doClick(e){
    if(window.history.pushState)
        openURL(e.getAttribute('href'), true);
    else
        window.open(e.getAttribute('href'), '_self');
}
function openURL(href, push){
    document.getElementById('content').innerHTML = href + ': ' + (push ? 'user' : 'browser'); 
    if(window.history.pushState){
        if(push)
            window.history.pushState({href: href}, 'your page title', href);
        else
            window.history.replaceState({href: href}, 'your page title', href);
    }
}
window.onpopstate = function(e){
    if(e.state)
        openURL(e.state.href);
};
</script>

Bu olsa olabilir hala (bazı şık navigasyon) iki kez ateş, önceki href karşı bir çek ile basitçe ele alınabilir.


1
Teşekkürler. Pjax çözümü, iOS'ta window.history.stateda boş olduğu için iOS 5.0 üzerinde çalışmayı durdurdu . Sadece e.state özelliğinin boş olup olmadığını kontrol etmek yeterli.
Husky

Bu çözümle ilgili bilinen sorunlar var mı? Test ettiğim tüm tarayıcılarda mükemmel çalışıyor.
Jacob Ewing

3

Bu benim çözümüm.

window.setTimeout(function() {
  window.addEventListener('popstate', function() {
    // ...
  });
}, 1000);

Ubuntu'daki Chromium 28'de benim için değil. Bazen olay hala ateşleniyor.
Jorge Suárez de Lis

Bunu kendim denedim, her zaman işe yaramıyor. Bazen popstate, ilk sayfa yüklemesine bağlı olarak zaman aşımından sonra tetiklenir
Andrew Burgess

1

İşte benim çözümüm:

var _firstload = true;
$(function(){
    window.onpopstate = function(event){
        var state = event.state;

        if(_firstload && !state){ 
            _firstload = false; 
        }
        else if(state){
            _firstload = false;
            // you should pass state.some_data to another function here
            alert('state was changed! back/forward button was pressed!');
        }
        else{
            _firstload = false;
            // you should inform some function that the original state returned
            alert('you returned back to the original state (the home state)');
        }
    }
})   


0

Kullanım durumunda event.state !== nullgeçmişte ilk yüklenen sayfaya geri dönmek mobil olmayan tarayıcılarda çalışmayacaktır. Ajax navigasyonunun ne zaman başladığını işaretlemek için sessionStorage kullanıyorum.

history.pushState(url, null, url);
sessionStorage.ajNavStarted = true;

window.addEventListener('popstate', function(e) {
    if (sessionStorage.ajNavStarted) {
        location.href = (e.state === null) ? location.href : e.state;
    }
}, false);


window.onload = function () {if (history.state === null) {history.replaceState (location.pathname + location.search + location.hash, null, location.pathname + location.search + location.hash); }}; window.addEventListener ('popstate', function (e) {if (e.state! == null) {location.href = e.state;}}, false);
illüzyonist

-1

Sunulan çözümlerin sayfanın yeniden yüklenmesinde bir sorunu var. Aşağıdakiler daha iyi çalışıyor gibi görünüyor, ancak yalnızca Firefox ve Chrome'u test ettim. e.event.stateVe arasında bir fark varmış gibi görünen gerçekliği kullanır window.history.state.

window.addEvent('popstate', function(e) {
    if(e.event.state) {
        window.location.reload(); // Event code
    }
});

e.eventtanımsız
chovy

-1

Karşı sorduğunuzu biliyorum, ancak bir milyon tarayıcı uyumsuzluğunu temizlediği için gerçekten sadece History.js kullanmalısınız. Manuel düzeltme rotasına, ancak daha sonra, yolun sonunda bulacağınız daha fazla sorun olduğunu bulmak için gittim. Bugünlerde gerçekten o kadar zor değil:

<script src="//cdnjs.cloudflare.com/ajax/libs/history.js/1.8/native.history.min.js" type="text/javascript"></script>

Ve api'yi https://github.com/browserstate/history.js adresinde okuyun.


-1

Bu benim için sorunu çözdü. Tek yaptığım, pageload sırasında tetiklenen popstate olayını kaçıracak kadar uzun süre işlevin yürütülmesini geciktiren bir zaman aşımı işlevi ayarlamaktı.

if (history && history.pushState) {
  setTimeout(function(){
    $(window).bind("popstate", function() {
      $.getScript(location.href);
    });
  },3000);
}

-2

Bir olay oluşturabilir ve yükleme işleyicinizden sonra onu tetikleyebilirsiniz.

var evt = document.createEvent("PopStateEvent");
evt.initPopStateEvent("popstate", false, false, { .. state object  ..});
window.dispatchEvent(evt);

Bu, Chrome / Safari'de biraz bozuldu, ancak yamayı WebKit'e gönderdim ve yakında kullanıma sunulacak, ancak bu "en doğru" yoldur.


-2

Bu benim için Firefox ve Chrome'da çalıştı

window.onpopstate = function(event) { //back button click
    console.log("onpopstate");
    if (event.state) {
        window.location.reload();
    }
};
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.