2011-04-22 28 views
7

你好,我正在用Javascript開發我自己的路由器API。它基於#FregmentIdentifiers(document.location.hash)進行路由。如何使用Javascript保存和恢復整個網站?

該api是almos完成,但我仍然在backbuttom事件工作。每當按下後臺按鈕並且哈希已經改變並且之前被看到時,舊內容將被恢復。

您是否知道一種保存和恢復所有內容的方法?

我在這裏的問題是,如果我保存和恢復document.body.innerHTML只有標記被恢復,但不是事件,例如, Google地圖停止工作。 我想克隆document.body或document.documentElement,但是JavaScript告訴我該字段沒有setter或者我的克隆無效。

編輯:

爲了畢竟清楚我的工作,我決定後我當前的代碼。 這個問題針對的是標有// TODO評論的部分。

function Router(){ 
var that = this; 
var router = this; 
var executionObservers = []; 
that.routes = []; 
this.registerRoute = function(route){ 
    that.routes.push(route); 
}; 
var history = null; 
this.init = function(){ 
    var i; 
    var identifier = document.location.hash; 
    history = new History(); 
    history.start(); 
    if(identifier.length > 0){ 
     identifier = identifier.substring(1,identifier.length); 
     for(i = 0; i< that.routes.length; i++){ 
      var route = that.routes[i]; 
      if(route.contains(identifier)){ 
       route.getAction(identifier)(route.getParams(identifier)); 
       return true; 
      } 
     } 
    } 
    return false; 
}; 
this.executed = function (identifier){ 
    var i; 
    for(i=0; i<executionObservers.length; i++){ 
     executionObservers[i](identifier); 
    } 
    document.location.hash = identifier; 
}; 

this.addExecutionObserver = function(observer){ 
    executionObservers.push(observer); 
}; 

function History(){ 
    var history = []; 
    var timeout = 200; 
    var lastAddedHash = null; 
    var loop = function(callback){ 
     var hash = window.location.hash; 
     window.setTimeout(
      function(){ 
       if(window.location.hash!=hash){ 
        hash = window.location.hash; 
        callback(hash); 
       } 
       loop(callback); 
      }, 
      timeout 
     ); 
    }; 
    this.add = function(hash){ 
     lastAddedHash = hash; 
     window.setTimeout(addCallback(hash), timeout);   
    }; 
    addCallback = function(hash){ 
     return function(){ 
      var i; 
      var found = false; 
      for(i =0; i< history.length&&!found; i++){ 
       if(history[i][1] == hash){ 
        found = true; 
        //TODO create backup 
        //history[i][0] = 
       } 
      } 
      if(!found){history.push(new Array(document.documentElement.cloneNode(true),hash));} 
     } 
    } 
    this.setTimeout = function(micoseconds){ 
     timeout = microseconds; 
    }; 
    started = false; 
    this.start = function(){ 
     if(!started){ 
      started = true; 
      loop(function(hash){ 
       var i; 
       if(lastAddedHash!=null&&hash!=lastAddedHash){ 
        for(i =0; i<history.length; i++){ 
         if(history[i][1] == hash){ 
          //TODO restore from backup 
          document.location.reload(); 
         } 
        } 
       } 
      }); 
     } 
    }; 
    router.addExecutionObserver(this.add); 
} 
} 

Router.instance = null; 
Router.getInstance = function(){ 
    if(Router.instance === null){ 
     Router.instance = new Router(); 
    } 
    return Router.instance; 
}; 

/** 
* @param getParams = function(identifier) 
* @param getIdentifier = function(params) 
* @param contains = function(identifier) 
*/ 
function Route(action, getParams, getIdentifier, contains){ 
    var that = this; 
    var router = Router.getInstance(); 
    this.contains = contains; 
    this.getParams = getParams; 
    this.getAction = function(){ 
     return action; 
    } 
    this.reExecute = function(identifier){ 
     action(getParams(identifier)); 
    }; 
    this.execute = function(params){ 
     action(params); 
     this.executed(params); 
    } 
    this.executed = function(params){ 
     router.executed('#' + getIdentifier(params)); 
    }; 
    this.register = function(){ 
     router.registerRoute(this); 
    }; 
} 
function PrefixedRouterConfig(prefix,paramRegexes){ 
    this.contains = function(identifier){ 
     var regex = "^" + prefix; 
     for(var i=0;i<paramRegexes.length;i++){ 
      regex+="_"+paramRegexes[i]; 
     } 
     regex +="$"; 
     var match = identifier.match(regex); 
     return match != null && (typeof match) == 'object' && (match[0] == identifier); 
    }; 
    this.getIdentifier = function(params){ 
     ret = prefix; 
     for(var i=0;i<params.length;i++){ 
      ret+="_"+params[i]; 
     } 
     return ret; 
    }; 
    this.getParams = function(identifier){ 
     var regex = "^" + prefix; 
     for(var i=0;i<paramRegexes.length;i++){ 
      regex+="_("+paramRegexes[i]+")"; 
     } 
     regex +="$"; 
     var matches = identifier.match(regex); 
     var ret = []; 
     for(var i=1;i<matches.length;i++){ 
      ret.push(matches[i]); 
     } 
     return ret; 
    }; 
} 

我的API的使用示例可以是這樣的:

config = new PrefixedRouterConfig('show_map',new Array("\\d+", "-?\\d+(?:\\.\\d+)?", "-?\\d+(?:\\.\\d+)?")); 
var ROUTE_SHOW_MAP = new Route(
    function(params){ 
     var zoom = params[0]; 
     var lat = params[1]; 
     var lng = params[2]; 
     MyGmapInterface.preparePage(-1); 
     addTabSelectedCallback(MyGmapInterface.tabLoaded); 
     addTabClosedCallback(MyGmapInterface.tabClosed); 
     MyGmapInterface.tabsLoaded = true; 
     MyGmapInterface.myMap = new MyMap(lat,lng,zoom,MyGmapInterface.getMapContainer(),MyGmapInterface.notCompatible); 
     MyGmapInterface.addNewCamMarkers(MyGmapInterface.loadCams()); 
     MyGmapInterface.initListeners(); 
     tabSelected(TAB_LEFT); 
    }, 
    config.getParams, 
    config.getIdentifier, 
    config.contains 
); 
ROUTE_SHOW_MAP.register(); 

畢竟Javascript文件包括(可註冊路由)我叫Router.getInstance()的init()。

當我在某個地方做一個存在路由的ajax請求時,我調用ROUTE_NAME.executed()來設置fregment標識符並註冊它的歷史記錄。

而且我有一個觀測器更新一些鏈接,其中用於直接翻譯,每當位置散列被改變()執行

+0

好吧,我已經開始了這個賞金。 50+對於那個給我這個問題的答案的人,顯示工作代碼。目前,我正在通過簡單的重新加載來解決後退按鈕事件,但我更喜歡的是從歷史記錄恢復。這意味着,標記和事件也可以被存儲和恢復。我只需要這裏的存儲和恢復邏輯。事件處理已經完成。或者你會說,處理它與重新加載更好? 關心和感謝。 – 2011-04-24 15:57:51

+2

你的賞金是浪費時間,你想要做的事情既非常困難也毫無意義。瀏覽器自己處理緩存的歷史頁面,而不是來自網站的「代碼」。您可以使用PHP來恢復前一頁,並傳入一個包含您希望在該頁面上設置的歷史參數的對象。說實話,一個基於片段標識符的'路由器API'聽起來可怕並且是史前的,你應該再投入一些時間來學習現代網絡實踐。 – GAgnew 2011-04-26 17:29:28

+0

我有一個JavaScript代碼,它爲每個ajax請求設置了唯一的細分標識符。通過細分標識符,整個頁面可以恢復。但是目前的後退按鈕在沒有重新加載的情況下無法運行。 – 2011-04-28 16:49:29

回答

2

除非你已經通過跟蹤他們的API(增加你的事件,如jQuery的確如此;請參閱http://api.jquery.com/clone#true),您將無法反思已添加的事件以便序列化/保留它們。

如果您不太可能選擇使用DOM用戶數據,您還需要setUserData()來序列化任何DOM用戶數據(或者再次,像jQuery一樣的庫來爲您進行跟蹤)。

+0

我問自己是否允許複製JQuery代碼而不使用JQuery,以及在哪裏可以找到clone()的代碼。 – 2011-04-22 12:12:47

+0

我無法在我的項目中使用jQuery。 – 2011-04-22 12:26:09

+0

你的意思是,你想知道jQuery的哪一部分處理克隆?你可以做的是基本上做一些跟蹤事件'var Event = {els:[],evs:[],cb:[],add:function(el,ev,cb){el.addEventListener(ev,cb,假); this.els.push(EL); this.evs.push(EV); this.cbs.push(CB); }};'然後你可以在'Event.evs'等內容上進行反省,找出已經添加的內容,並在你進行克隆時將其添加回來。 – 2011-04-22 12:32:28

8

這與刷新的情況相同,因此您應該重新使用該系統。

基本上你的散列必須包含足夠的信息來重建整個頁面。當然,有時你需要保存一些用戶輸入來重建頁面。這就是localStorage的用途(用於IE的userData)

+0

我寧願將頁面緩存在用戶的內存中,而不是將其從服務器重新加載! – 2011-04-22 12:06:05

+2

如果你的配置是正確的,頁面請求應該給出304 Not Modified響應。所以瀏覽器從緩存中讀取頁面。 – 2011-04-22 14:19:19

+0

到目前爲止還不錯,但這意味着額外的流量對我來說。我的想法是在開發本地緩存時,我可以減少服務器請求。 – 2011-05-04 17:43:43

1

你看過jQuery history plugin?我知道在你提到的另一篇文章中,jQuery不是一種選擇,但你可能會模仿他們的方法。

我不是這方面的專家,但由於瀏覽器的實現方式不同,我相信這並不是微不足道的以使跨瀏覽器工作。

主要網站:http://tkyk.github.com/jquery-history-plugin/#

演示頁:http://www.serpere.info/jquery-history-plugin/samples/ajax/

+0

我沒有看到他們在保存和恢復的位置,我是否失明? https://github.com/tkyk/jquery-history-plugin/raw/master/jquery.history.js – 2011-04-22 23:48:06

+0

這是利用*內置*瀏覽器歷史實現,而不是更蠻力的方式來存儲以某種方式在JS內存中的頁面。基本上,您指示瀏覽器存儲它通常不認爲值得存儲的動態URL /頁面狀態。正確完成後,後退按鈕無需額外工作。 (正如你所看到的,你需要創造性地使用跨瀏覽器,在某些情況下使用iFrames。)* *認爲將新(動態)位置設置到瀏覽器歷史記錄中的代碼是'implements。*。load );'。 – peteorpeter 2011-04-25 20:39:26

1

你想要什麼幾乎是不可能的,你可以做到這一點,如果你使用像jQuery庫來構建你的頁面的事件,如brettz9建議,但在你的情況下,你正在使用谷歌地圖和其他外部庫,所以它不是一個選項。 這裏要走的路是知道你在哪個頁面上,並執行初始化頁面的JavaScript,就好像它是第一次加載頁面一樣。爲此,您不僅需要跟蹤頁面的html,還需要跟蹤頁面加載時執行的javascript。

0

我知道的唯一方法是欺騙瀏覽器,使其相信每個有價值的事件都是一個新頁面。你可以通過使用window.location加載一個新頁面,或者使用get(= query string)提交一個表單。新位置或表單數據必須包含顯示正確信息所需的所有內容。這有它的不足之處 - 這是我的觀點:

好:

  • 後退和前進按鈕的工作
  • 視圖數據的可書籤
  • 與謹慎使用查詢字符串的數據,它提供了一個API到你的系統

壞:

  • 每一個有價值的事件都必須經過頁面上傳
  • 不能利用ajax
  • 你必須設計一種方法來編碼查詢字符串形式的系統的每個狀態。
2

怎麼樣在IFRAME擁有一切和鏡像所有父網址片段更改,然後作用於iframe中。儘管這裏可能會出現跨站點腳本問題。

因爲瀏覽器緩存 - 它決定是否從內存加載 - 不是你加載它是非常困難的。因此,如果上述內容不是選項,那麼您將創建該選項的唯一方法是通過瀏覽器擴展,該擴展程序偵聽選項卡後退按鈕事件並採取相應措施。