pushState yinelenen geçmiş girişleri oluşturma ve öncekilerin üzerine yazma


15

Geçmişi de güncellerken sayfalarda gezinmek için history pushStateve replaceStateyöntemlerini kullanan bir web uygulaması oluşturdum .

Betiğin kendisi neredeyse mükemmel çalışır; sayfaları doğru yükler ve atılmaları gerektiğinde sayfa hataları atar. Ancak, pushStategeçmişe birden fazla, yinelenen girişleri (ve daha önce girişleri değiştirecek) itecek garip bir sorun fark ettim .

Örneğin, aşağıdakileri yaptığımı varsayalım (sırayla):

  1. İndex.php dosyasını yükle (geçmiş: Dizin olacak)

  2. Profile.php dosyasına gidin (geçmiş: Profil, Dizin)

  3. Search.php dosyasına gidin (geçmiş: Arama, Arama, Dizin)

  4. Dashboard.php adresine gidin

Sonunda, tarihimde ortaya çıkacak olan budur (en yeniden en eskiye doğru):

Gösterge
Tablosu
Gösterge Tablosu Gösterge Tablosu
Arama
Dizini

Buradaki sorun, bir kullanıcı ileri veya geri düğmelerini tıklattığında, yanlış sayfaya yönlendirilecek veya bir kez geri gitmek için birden çok kez tıklamaları gerekecektir. Bu, gidip tarihlerini kontrol etmeleri anlamsız olacaktır.

Şimdiye kadar sahip olduğum şey bu:

var Traveller = function(){
    this._initialised = false;

    this._pageData = null;
    this._pageRequest = null;

    this._history = [];
    this._currentPath = null;
    this.abort = function(){
        if(this._pageRequest){
            this._pageRequest.abort();
        }
    };
    // initialise traveller (call replaceState on load instead of pushState)
    return this.init();
};

/*1*/Traveller.prototype.init = function(){
    // get full pathname and request the relevant page to load up
    this._initialLoadPath = (window.location.pathname + window.location.search);
    this.send(this._initialLoadPath);
};
/*2*/Traveller.prototype.send = function(path){
    this._currentPath = path.replace(/^\/+|\/+$/g, "");

    // abort any running requests to prevent multiple
    // pages from being loaded into the DOM
    this.abort();

    return this._pageRequest = _ajax({
        url: path,
        dataType: "json",
        success: function(response){
            // render the page to the dom using the json data returned
            // (this part has been skipped in the render method as it
            // doesn't involve manipulating the history object at all
            window.Traveller.render(response);
        }
    });
};
/*3*/Traveller.prototype.render = function(data){
    this._pageData = data;
    this.updateHistory();
};
/*4*/Traveller.prototype.updateHistory = function(){
    /* example _pageData would be:
    {
        "page": {
            "title": "This is a title",
            "styles": [ "stylea.css", "styleb.css" ],
            "scripts": [ "scripta.js", "scriptb.js" ]
        }
    }
    */
    var state = this._pageData;
    if(!this._initialised){
        window.history.replaceState(state, state.title, "/" + this._currentPath);
        this._initialised = true;
    } else {
        window.history.pushState(state, state.title, "/" + this._currentPath);  
    }
    document.title = state.title;
};

Traveller.prototype.redirect = function(href){
    this.send(href);
};

// initialise traveller
window.Traveller = new Traveller();

document.addEventListener("click", function(event){
    if(event.target.tagName === "a"){
        var link = event.target;
        if(link.target !== "_blank" && link.href !== "#"){
            event.preventDefault();
            // example link would be /profile.php
            window.Traveller.redirect(link.href);
        }
    }
});

Tüm yardımlar takdir
ediliyor , Şerefe.


Öyleyse bir tarih değişikliğini sadece hedef son girişten farklıysa yaymak mı istiyorsunuz?
Aluan Haddad

Bu rastgele görünüyor mu? Veya belirli sayfalar her zaman yinelenen geçmiş girişlerine neden olan sayfalardır.
SamVK

@AluanHaddad hayır Yalnızca bir tarih değişikliği varsa (yani bir kullanıcı sitede gezinirken) bir tarih değişikliğini yaymak istiyorum. Normal bir durumda olanlara benzer ... (dizinden profilden StackOverflow'da arama yapmak geçmişime tam olarak dönecektir)
GROVER.

@SVVK İlk yükleme sayfasından (ör. İndex.php) geçtikten sonra hangi sayfanın önemi yoktur, girişler kendilerini çoğaltacaktır.
GROVER.

1
Pekala, bu yüzden yorumlarda yazmaktan pek emin değilim. Görüyorum ki, updateHistoryişlevinize tarayıcı geçmişi öğeleri ekliyorsunuz . Şimdi updateHistory, 1, iki kez denilen almak olabilir Eğer Traveler'ı başlatılıyor yaparken ( window.Traveller = new Traveller();, constructor-> init-> send-> render-> updateHistory), sonra da tarafından redirectgelen clickEventListener. Ben test etmedim, sadece vahşi tahmin, bu yüzden bir cevap olarak değil bir yorum olarak ekleyerek.
Akshit Arora

Yanıtlar:


3

Eğer bir var mı onpopstate işleyicisi?

Cevabınız evet ise, tarihe itip girmediğinizi de kontrol edin. Geçmiş listesindeki bazı girişlerin kaldırılması / değiştirilmesi büyük bir işaret olabilir. Gerçekten, şu SO cevabına bakın :

History.pushState () yönteminin yeni bir durumu en yeni geçmiş durumu olarak ayarladığını unutmayın. Ve belirlediğiniz durumlar arasında gezinirken (geri / ileri) window.onpopstate çağrılır.

Window.onpopstate çağrıldığında PushState'i zorlamayın, çünkü bu yeni durumu son durum olarak ayarlayacaktır ve daha sonra ileri gidilecek bir şey yoktur.

Bir keresinde tarif ettiğinizle aynı problemi yaşadım, ancak aslında popState işleyicisini tetikleyecek olan hatayı anlamaya çalışmak için ileri geri gitmemden kaynaklandı. Bu işleyiciden, history.push olarak adlandırılır. Sonunda, bazı yinelenen girişlerim vardı ve bazıları mantıklı bir açıklama yapmadan eksikti.

History.push çağrısını kaldırdım, bir tarihle değiştirdim. Bazı koşulları kontrol ettikten sonra değiştirin ve bir cazibe gibi çalıştıktan sonra :)

DÜZENLE -> İPUCU

Hangi kodu history.pushState çağırdığını bulamıyorsanız:

History.pushState ve replaceState işlevlerinin üzerine aşağıdaki kodu yazmayı deneyin:

window.pushStateOriginal = window.history.pushState.bind(window.history);
window.history.pushState = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    let allowPush  = true;
    debugger;
    if (allowPush ) {
        window.pushStateOriginal(...args);
    }
}
//the same for replaceState
window.replaceStateOriginal = window.history.replaceState.bind(window.history);
window.history.replaceState = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    let allowReplace  = true;
    debugger;
    if (allowReplace) {
        window.replaceStateOriginal(...args);
    }
}

Sonra kesme noktaları her tetiklendiğinde, çağrı yığını bir göz atın.

Konsolda, bir pushState'i engellemek istiyorsanız, devam etmeden önce allowPush = false;veya allowReplace = false;devam etmeden önce girin . Bu şekilde, herhangi bir geçmişi kaçırmayacaksınız. PushState ve yukarı gidip çağıran kodu bulabilirsiniz :)


Yapıyorum, ancak geçmişi hiç itmiyor veya değiştirmiyor: / sadece sayfayı işler
GROVER.

Gerçekten garip. History.push işlevinin üzerine aşağıdaki kodu yazmayı deneyin: const hP = history.pushState history.pushState = (loc) => {debugger; hP (loc)} ve history.replaceState için aynısını yapın. Sonra kesme noktaları her
tetiklendiğinde

Çağrı yığınını kontrol ettim ve her şey doğru bir şekilde yapılıyor gibi görünüyor - ancak tarih çalışmak istemiyor. Mozilla neden her şeyi bu kadar zorlaştırmalı :(
GROVER.

Teşekkürler :) Ve Chrome'u denerseniz, hangi sonuçlarınız var?
Bonjour123
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.