如何在Javascript的popstate事件上执行location.reload()时维护浏览器历史记录

如何在Javascript的popstate事件上执行location.reload()时维护浏览器历史记录,javascript,jquery,redirect,browser-history,Javascript,Jquery,Redirect,Browser History,因此,我使用history.pushState函数生成所有URL。我没有为此使用任何服务器端技术。我只从服务器获取一次所有数据,然后使用Javascript生成URL 现在我遇到的问题是使用popstate函数,它基本上模仿了浏览器的后退按钮行为 只需点击一次后退按钮,一切正常 $(window).on('popstate', function (event) { location.reload(true); //window.location.href

因此,我使用
history.pushState
函数生成所有URL。我没有为此使用任何服务器端技术。我只从服务器获取一次所有数据,然后使用
Javascript
生成URL

现在我遇到的问题是使用
popstate
函数,它基本上模仿了浏览器的后退按钮行为

只需点击一次后退按钮,一切正常

    $(window).on('popstate', function (event) {
        location.reload(true);
        //window.location.href = document.location;
        //console.log("location: " + document.location);
    });
在一次后退按钮事件后,我无法查看浏览器的任何以前历史记录。我假设这是因为
location.reload
方法。我还尝试存储我的所有位置,然后执行重定向,但同样的事情是,浏览器历史记录在刷新后丢失

有没有一种方法可以让浏览器在多次点击后退按钮时不会丢失历史记录


如果有一个
JQuery
选项,那么如果有人能分享他们在这方面的知识就太好了。

嗯,这是不可能的。 我考虑的唯一技巧是在触发
popstate
事件时更改
window.location.href
,并将历史记录作为URL参数传递

$(window).on('popstate', function (event) {
    window.location.href = 'https://here.com?history = ' + yourHistory;
});
您需要弄清楚如何使用此方法发送历史记录,并在到达新页面时进行处理,以便在用户再次单击“上一步”按钮时再次发送历史记录。
您还必须确保生成的URL不会太长。

我编写了一个简单的插件,它是围绕我在项目中使用的URL构建的。它会满足你的要求

        // instantiate the QueryString Class.            
        qsObj=new QueryString({

            onPopstate: function(qsParams, data, dataAge, e) {
                // This function is called on any window.popstate event
                //   -- triggered when user click browser's back or forward button.
                //   -- triggered when JS manipulates history.back() or history.forward()
                //   -- NOT triggered when any of the QueryString methods are called (per the native behavior of the HTML5.history API which this class uses.)
                //   -- Might be triggered on page load in some browsers.  You can handle this by checking for the existence of data, eg:  if(data){ 'do something with the data' }else{ 'move along nothing to do here' }

                console.log('executing onPopstate function');
                console.log('page query string parameters are: ', qsParams);
                console.log('stored page data is: ', data);
                console.log('dataAge is:', dataAge, ' seconds old');

                // do stuff..

                if(data) {
                    if(dataAge && dataAge<=20) {
                        // use the stored data..

                    } else {
                        // it's old data get new..
                        
                    }
                } else {
                    // the current page has no stored data..
                }
            },

            onDocumentReady: function(qsParams) {
                // Document ready is only called when a page is first browsed to or the page is refreshed.
                // Navigating the history stack (clicking back/forward) does NOT refire document ready.
                // Use this function to handle any parameters given in the URL and sent as an object of key/value pairs as the first and only parameter to this function.

                console.log('executing onDocumentReady function');
                console.log('page query string parameters are:',qsParams);

                // do stuff..
            }

        });
//实例化QueryString类。
qsObj=新查询字符串({
onPopstate:函数(qsparms、data、dataAge、e){
//在任何window.popstate事件上调用此函数
//--当用户单击浏览器的后退或前进按钮时触发。
//--当JS操纵history.back()或history.forward()时触发
//--在调用任何QueryString方法时不会触发(根据该类使用的HTML5.history API的本机行为)
//--可能在某些浏览器中的页面加载时触发。您可以通过检查数据的存在来处理此问题,例如:if(data){“对数据执行某些操作”}else{“在此处不执行任何操作”}
log('executing onPopstate function');
log('页面查询字符串参数为:',qsparms);
log('存储的页面数据为:',数据);
log('dataAge为',dataAge为'seconds old');
//做事。。
如果(数据){

如果(dataAge&&dataAge)您是否尝试将所有位置存储到
localStorage
,然后使用重定向?您需要某种持久性来存储历史记录。Cookie、localStorage、sessionStorage(仅在页面关闭之前有效),或服务器端存储。@PeterDarmis如果我使用
localStorage
,跟踪上次访问的历史记录不是一项乏味的任务吗。您有什么想分享的策略吗?@Codyface这是正确的,但是使用上述方法的跟踪部分会非常困难。如果用户单击e后退按钮3-4次?我将如何在我的持久性存储中跟踪这些事件?@RahulSharma历史状态保存在历史对象中。在每个访问的页面上也在localStorage中传递这些状态。如果历史被清除,您可以从localStorage还原它。感谢您的回答。我将尝试一下,并让您知道它是否在我的场景中起作用o、 你能给我一个这个插件的示例用法吗?ThanksI创建了一个fiddle,演示了它的用法。但是,iframe fiddle用于呈现HTML页面的功能阻止了演示的正常运行。如果你将HTML和javascript复制到自己的页面并运行它,那么演示就可以正常运行。我还更新了我的帖子,其中显示如何在最基本的术语中使用插件我无法让它在我的终端运行(也许我无法获得它的jist)但我仍然会奖励你的努力,我相信你的插件在我的场景中工作得很好。好的,我有一些来自人们的建议,所以我会尝试一下,看看是否真的不可能。
// == QueryString class =============================================================================================
//
//    Modifies the query string portion of a browsers URL, updates the browsers history stack - saving page data 
//    along with it, all without reloading the page.
//    Can be used to simply obtain querystring parameter values as well.    
//
//    == Instantiate: ================================
//
//    var qsObj=new QueryString(
//
//        onPopstate: function(qsParams, data, dataAge, e) {
//
//            // This function is called on any window.popstate event
//            //   -- triggered when user click browser's back or forward button.
//            //   -- triggered when JS manipulates history.back() or history.forward()
//            //   -- NOT triggered when any of the QueryString methods are called (per the native behavior of the
//            //      HTML5.history API which this class uses.)
//            //   -- Might be triggered on page load in some browsers.  You can handle this by checking for the 
//            //      existence of data, eg:  
//            //            if(data){ 'do something with the data' }else{ 'move along nothing to do here' }
//
//            // -- qsParams: is an object that contains the current pages query string paramters as key:value pairs.
//            // -- data: is an object that contains any page data that was stored at the time that this page was added
//            //    to the history stack via this class (or any HTML5 history API method),  otherwise this value is NULL!
//            // -- dataAge:  null if data is null or the page data was not added via the methods in this class, 
//            //    otherwise the value is the age of the data in seconds.
//            // -- e: the event object if you want it.
//
//            if(data){
//                if(dataAge && dataAge <= 600){ // do it this way otherwise you'll error out if dataAge is null.
//                    // There is data and it is less than 10 minutes old.
//                    // do stuff with it..
//                }
//            }
//        },
//
//        onDocumentReady: function(qsParams){
//            // Document ready is only called when a page is first browsed to or the page is refreshed.
//            // Navigating the history stack (clicking back/forward) does NOT refire document ready.
//            // Use this function to handle any parameters given in the URL and sent as an object of key/value pairs as 
//            // the first and only parameter to this function.
//
//            // do stuff with any qsParams given in the URL..
//
//        }
//
//    });
//
//
//    == The following methods are available: =======================================
//
//
//    var qsParams = qsObj.parseQueryString(); // returns an object that contains the key/value pairs of all the query 
//                                             // string parameter/values contained in the current URL, or an empty object
//                                             // if there are none.
//
//
//    qsObj.update({
//
//        // Use this method to add/remove query string parameters from the URL and at the same time update, or add to, the 
//           browser history stack with the ability to also save page data in with the history.
//
//        opType: 'auto',
//        //  -- Optional. Allowed values: ['replace'|'push'|'auto'], Default is 'auto' unless 'push' or 'replace' is 
//        //     specifically given.
//        //  -- 'push':    Adds the new URL and any page data onto the browser history stack.
//        //  -- 'replace': Overwrites the current page history in the stack with the new URL and/or page data
//        //  -- 'auto':    compares the initial qs parameters with the updated qs parameters and if they are the same 
//        //      does a 'replace', if they are different does a 'push'.
//
//        qsParams: {
//            hello: 'world',
//            another: 'pair'
//        },
//        //  -- Optional. Object that contains key/value pairs to add to the query string portion of the URL.
//        //  -- Will entirely replace what is/isn't currently in the query string with the given key/value pairs.
//        //  -- The parameters contained in the url querystring will be made, or unmade, based on the key/value pairs 
//        //     included here so be sure to include all of the pairs that you want to show in the URL each time.
//
//
//        data: {
//           key1: 'value1',
//           key2: 'value2'
//        }
//        // Optional, Object that contains key/value pairs to store as page data in the browser history stack for this page.
//
//        // ** If qsParams and data are ommitted then nothing silently happens. (This is not the same as given but empty, 
//        //    in which case something happens.)
//
//    });
//
//
//    qsObj.Clear({
//
//       // Use this method to remove all query string parameters from the URL and at the same time update, or add to, the
//       // browser history stack with the ability to also save page data in with the history.
//
//       optype: 'auto' // optional, defaults to auto.
//
//       data: {} // Optional, defaults to empty object {}.
//    });
//
// =========================================================================================================================
; (function () {
var Def = function () { return constructor.apply(this, arguments); };
var attr = Def.prototype;

//== list attributes
attr.popstateCallback = null;
attr.docreadyCallback = null;
attr.skipParseOnInit = false;
attr.currentQS;

//== Construct
function constructor(settings) {
    if (typeof settings === 'object') {
        if ('onPopstate' in settings && typeof settings.onPopstate === 'function') {
            this.popstateCallback = settings.onPopstate;
        }
        if ('onDocumentReady' in settings && typeof settings.onDocumentReady === 'function') {
            this.docreadyCallback = settings.onDocumentReady;
        }
    }
    if (this.skipParseOnInit !== true) {
        this.currentQS = this.parseQueryString();
    }
    var self = this;
    if (typeof this.popstateCallback === 'function') {
        $(window).on('popstate', function (e) {
            var data = null;
            var dataAge = null;
            if (typeof e === 'object' && 'originalEvent' in e && typeof e.originalEvent === 'object' && 'state' in e.originalEvent && e.originalEvent.state && typeof e.originalEvent.state === 'object') {

                data = e.originalEvent.state;
                if ('_qst_' in data) {
                    dataAge = ((new Date).getTime() - e.originalEvent.state['_qst_']) / 1000; // determine how old the data is, in seconds
                    delete data['_qst_'];
                }
            }
            var qsparams = self.parseQueryString();
            self.popstateCallback(qsparams, data, dataAge, e);
        });
    }

    if (typeof this.docreadyCallback === 'function') {
        $(document).ready(function () {
            self.docreadyCallback(self.currentQS);
        });
    }
}

//== Define methods ============================================================================

attr.parseQueryString = function (url) {
    var pairs, t, i, l;
    var qs = '';
    if (url === undefined) {
        var loc = window.history.location || window.location;
        qs = loc.search.replace(/^\?/, '');
    } else {
        var p = url.split('?');
        if (p.length === 2) {
            qs = p[1];
        }
    }
    var r = {};
    if (qs === '') {
        return r;
    }
    // Split into key/value pairs
    pairs = qs.split("&");
    // Convert the array of strings into an object        
    for (i = 0, l = pairs.length; i < l; i++) {
        t = pairs[i].split('=');
        var x = decodeURI(t[1]);
        r[t[0]] = x;
    }
    return r;
};

//-- Get a querystring value from it's key name         
attr.getValueFromKey = function (key) {
    var qs = this.parseQueryString();
    if (key in qs) {
        return decodeURIComponent(qs[key].replace(/\+/g, " "));
    } else {
        return null;
    }
};

//-- if urlValue is given then qsParams are ignored.    
attr.update = function (params) {
    if (typeof params !== 'object') { return; }

    var urlValue = null;
    var data = {};
    if ('data' in params) {
        data = params.data;
        urlValue = '';
    }

    var opType = 'opType' in params ? params.opType : 'auto';

    if ('urlValue' in params && typeof params.urlValue === 'string') {
        urlValue = params.urlValue;
        if (opType === 'auto') {
            var loc = window.history.location || window.location;
            if (loc.protocol + '//' + loc.host + loc.pathname + loc.search === urlValue || loc.pathname + loc.search === urlValue) {
                opType = 'replace'; // same URL, replace
            } else {
                opType = 'push'; // different URL, push
            }
        }
    } else if ('qsParams' in params && typeof params.qsParams === 'object') {

        var pairs = [];
        for (var key in params.qsParams) {
            pairs.push(key + '=' + params.qsParams[key]);
        }
        urlValue = '?' + pairs.join('&', pairs);
        if (opType === 'auto') {
            if (this.compareQsObjects(params.qsParams, this.currentQS) === false) { // different                    
                this.currentQS = params.qsParams;
                opType = 'push';
            } else { // same                    
                opType = 'replace';
            }
        }
    }
    this.replaceOrPush(urlValue, data, opType);
};

//== Add querystring
//-- just an alias to update
attr.add = function (params) {
    return this.update(params);
};

//== Remove specified querystring parameters
//   -- Use clear() method to remove ALL query string parameters
attr.remove = function (params) {
    var urlValue = null;
    var qsChanged = false;
    if ('qsParams' in params && params.qsParams.length > 0) {
        var qs = this.parseQueryString();
        var key;
        for (var i = 0, l = params.qsParams.length; i < l; ++i) {
            key = params.qsParams[i];
            if (key in qs) {
                delete qs[key];
                qsChanged = true;
            }
        }
    }
    if (qsChanged === true) {
        var pairs = [];
        for (key in qs) {
            pairs.push(key + '=' + qs[key]);
        }
        urlValue = '?' + pairs.join('&', pairs);
        var data = 'data' in params ? params.data : {};
        var opType = 'opType' in params ? params.opType : '';
        this.replaceOrPush(urlValue, data, opType);
    }
    return;
};

//== Delete querystring
//-- just an alias to remove
attr.delete = function (params) {
    return this.remove(params);
};

//== Removes all query string parameters.
//   Use remove() method to remove just the given parameters 
attr.clear = function (params) {
    params = typeof params === 'undefined' ? {} : params;
    var urlValue = window.history.location || window.location;
    urlValue = urlValue.protocol + '//' + urlValue.host + urlValue.pathname;
    var data = 'data' in params ? params.data : {};
    var opType = 'opType' in params ? params.opType : '';
    this.replaceOrPush(urlValue, data, opType);
    return;
};

//== Simple wrapper to the HTML5 history API's replaceState() method.
//   --also used internally
attr.replaceState = function (urlValue, data) {
    if (typeof urlValue !== 'string') {
        return;
    }
    if (typeof data !== 'object') {
        data = {};
    }
    data['_qst_'] = (new Date).getTime(); // store a timestamp value        
    history.replaceState(data, '', urlValue);
};

//== Simple wrapper to the HTML5 history API's pushState() method.
//   --also used internally
attr.pushState = function (urlValue, data) {
    if (typeof urlValue !== 'string') {
        return;
    }
    if (typeof data !== 'object') {
        data = {};
    }
    data['_qst_'] = (new Date).getTime(); // store a timestamp value        
    history.pushState(data, '', urlValue);
};

//-- internal use - simple gatekeeper to decide if there is anything to do and will default to 'replace' opType if this value is not given.
attr.replaceOrPush = function (urlValue, data, opType) {
    // is there anything to do?
    if (typeof urlValue === 'string' || typeof data === 'object') {
        // yes, what type of operation are we going to do?
        if (opType === 'push') {
            this.pushState(urlValue, data);
        } else {
            this.replaceState(urlValue, data);
        }
    }
    return;
};

// == internal use - compares the existing qs with a potentially updated one to see if they are the same (returns true) or not (returns false)
attr.compareQsObjects = function (a, b) {
    if (typeof a === 'object' && typeof b === 'object') {
        var aa = [];
        var bb = [];
        for (k in a) {
            aa.push(k + a[k]);
        }
        aa.sort();
        for (k in b) {
            bb.push(k + b[k]);
        }
        bb.sort();
        if (aa.join('') !== bb.join('')) { return false; }
        return true;
    }
    return null;
};

//unleash your class
window.QueryString = Def;