【问题标题】:Can you get the previous history state object in js?能不能在js中获取之前的历史状态对象?
【发布时间】:2016-04-10 03:26:43
【问题描述】:

当我点击后退或前进按钮并触发 popstate 事件时,我可以获得前一个状态的状态对象吗?不是 e.state 提供的状态对象,而是我刚刚返回/转发的状态对象?

或者,我可以检测按下的是后退按钮还是前进按钮?

我需要这个,因为我有多个子系统都需要同时使用 js 历史记录,我当然可以在推送时保存所有子系统的状态,但是当我弹出时,我必须将状态恢复为所有这些也是不受欢迎的,因为它会重置我不需要的对象。例如

state 1 = {a:1, b:1, c:1}
push a = 2
state 2 = {a:2, b:1, c:1}
hit back button, popstate fires state 1 to me
restart a with 1, b with 1, c with 1 while I only need to restart a

另类幻想解决方案

....
hit back button, popstate fires state 1 to me
I also get state 2 (the one I just moved away from) through some black magic
do a differential comparison between the 2 states, find out that I only need to modify a
restart a with 1

编辑:哦,另外,澄清一下,没有简单的方法可以检查页面上当前的状态 a、b 和 c(因为它比 abc 和 123 复杂一点)

【问题讨论】:

    标签: javascript


    【解决方案1】:

    答案是装饰pushState来记录应用程序中的当前状态。所以,像:

    var app = {
        currentState: false,
        originalPushState: false,
    
        pushState : function(state, title, href) {
            app.currentState = state;
            app.originalPushState.call(history, state, title, href);
        },
    
        onPopstate : function(event) {
            var newState = event.state;
            var oldState = app.currentState;
            app.currentState = newState;
    
            // you now have the current state and the state you came from
        },
    
        init : function() {
            window.onpopstate = app.onPopstate;
            app.originalPushState = history.pushState;
            history.pushState = app.pushState;
        }
    }
    app.init();
    

    顺便说一句,popstate 非常令人困惑。当您弹出一个常规数组时,您会得到弹出的值,而不是现在位于其末尾的值,因此在语义上它有点奇怪。此外,history.state 始终包含当前状态,因此event.state 是多余的。设计师的奇怪选择,我想说,如果 event.popped 是指向实际弹出页面状态的指针(可能仅在与您的站点位于同一域中时可用),则更明智。

    【讨论】:

      【解决方案2】:

      onpopstate 在状态更改后被触发,因此您无法获取原始状态,但您可以反过来做,例如拥有状态副本和当前状态指示器,并在 @ 中更新您的副本987654322@手动。

      【讨论】:

      • 这不是一个真正的答案。你将如何编码?
      • 嘿@Tofandel,Richard J 的回答正是我建议的实现:自己维护副本,手动更新副本。只是他把它做成了一个装饰器,我可能认为这不是一个好习惯。
      • @dz902 你建议用什么实现来代替装饰器?
      • @AliMertCakar 也许只是避免覆盖内置函数。
      • @dz902 我并不是在争论,只是指出至少添加伪代码是件好事。
      【解决方案3】:

      扩展历史对象

      这是我的来源,你可以从https://gist.github.com/HasanDelibas/12050fc59d675181ea973d21f882081a看到完整的资源


      这个库包含:

      • history.states -> 获取状态列表
      • history.stateIndex -> 当前状态索引
      • “historyChange”事件 -> 检测历史变化(from,to,side="back"|"forward")
      • 重要! state 必须是 history.pushState( **state**, ...) 的对象
      (function(){
        let stateSymbol = "__state__index__";
        history.stateIndex =-1;
        history.states=[];
        let pushState = history.pushState;
        function add(data,title,url){
          if(data==null) data={};
          if(typeof data!="object") data={data:data};
          data[stateSymbol] = (history.stateIndex+1);
          history.states.splice(history.stateIndex+1,0,[data,title,url])
          history.states.splice(history.stateIndex+2)
          history.stateIndex++;
        }
        history.pushState =function(data,title,url=null){
          add(data,title,url);
          pushState.bind(history)(data,title,url);
        }
        addEventListener("popstate",function(e){
          var eventObject= {};
          var newStateIndex =  e.state!=null ? e.state[stateSymbol] : -1;
          eventObject.from = history.states[history.stateIndex];
          eventObject.to   = newStateIndex>-1 ? history.states[newStateIndex] : null;
          eventObject.side = history.stateIndex>newStateIndex ? "back" : "forward"; 
          if( newStateIndex > -1 && !(newStateIndex in history.states) ){
            add(history.state,"",window.location.href);
          }
          window.dispatchEvent(new CustomEvent("historyChange", {detail: eventObject} ))
          history.stateIndex = e.state!=null ? e.state[stateSymbol] : -1;
        });
      })();
      

      现在您可以使用history.states 对象获取所有状态, 并使用addEventListener("popstate",function(e))检测历史变化

      使用

      /**
        * @param e.detail.from [data,title,url]
        * @param e.detail.to   [data,title,url]
        * @param e.detail.side "back" | "forward"
        */
      addEventListener("historyChange",function(e){
        var from = e.detail.from; // [ data , title , url ]
        var to   = e.detail.to;   // [ data , title , url ]
        var side = e.detail.side; // "back" | "forward"
        console.log( `You changed history. Side is ${e.detail.side}.\nFrom:${e.detail.from[2]}\nTo:${e.detail.to[2]}`)
      })
      
      
      history.pushState("1", "DENEME-TEST" ,"?1");
      history.pushState("2", "DENEME-TEST" ,"?2");
      // list of history states
      console.log( history.states )
      /*
      [
        [ {...} ,  "DENEME-TEST" ,"?1" ]
        [ {...} ,  "DENEME-TEST" ,"?2" ]
      ]
      */
      // get history current state index
      console.log( history.stateIndex )
      /*
      1
      */
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-17
        • 1970-01-01
        • 1970-01-01
        • 2013-01-25
        • 2020-11-16
        • 1970-01-01
        相关资源
        最近更新 更多