【问题标题】:How to save and restore the whole website using Javascript?如何使用 Javascript 保存和恢复整个网站?
【发布时间】:2011-04-22 10:59:44
【问题描述】:

您好,我正在用 Javascript 开发自己的路由器 api。它基于#FregmentIdentifiers (document.location.hash) 进行路由。

api 已经完成了,但我仍在处理 backbuttom 事件。每当按下 backbuttom 并且 hash 发生了变化,并且之前被看到过,旧的内容就会被恢复。

您知道保存和恢复所有内容的方法吗?

我的问题是,如果我保存和恢复 document.body.innerHTML 只恢复标记而不恢复事件,例如谷歌地图停止工作。 我试图克隆 document.body 或 document.documentElement 但 javascript 要么告诉我该字段没有设置器,要么我的克隆无效。

编辑:

为了弄清楚我所做的一切,我决定发布我当前的代码。 该问题针对标有 //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() 来设置片段标识符并将其注册到历史记录中。

此外,我有一个观察者,它更新一些用于直接翻译的链接,每当位置哈希被执行()更改时

【问题讨论】:

  • 好的,我已经开始赏金了。 50+ 对于给我这个问题的答案的人,显示工作代码。目前我正在通过简单的重新加载来解决处理后退按钮事件,但我更喜欢从历史中恢复。这意味着,标记和事件也可以被存储和恢复。我只需要这里的存储和恢复逻辑。事件处理已经完成。或者你会说重新加载处理它会更好吗?问候和感谢。
  • 你的赏金是浪费时间,你想做的事情既非常困难又毫无意义。浏览器本身处理缓存的历史页面,而不是来自网站的“代码”。您可以使用 PHP 恢复前一页,并传入一个包含您希望在该页上设置的历史参数的对象。老实说,基于片段标识符构建的“路由器 api”听起来很可怕和史前,你应该重新投入一些时间来学习现代网络实践。
  • 我拥有的是 javascript 代码,它是每个 ajax 请求的唯一片段标识符。通过片段标识符,完整的页面是可恢复的。但是现在返回按钮在没有重新加载的情况下不起作用。
  • @tokam 我要说的是,这是错误的。我理解你试图完成什么,我只是不明白你为什么不必要地试图重新发明轮子。总体目标是什么?你想实现什么?为什么?
  • 听哈希变化就够了吗?对此有 JS 解决方案:benalman.com/projects/jquery-bbq-plugin

标签: javascript html javascript-events


【解决方案1】:

这与刷新的情况相同,因此您应该重新使用该系统。

基本上,您的哈希必须包含足够的信息来重建整个页面。当然,有时您需要保存一些用户输入来重建页面。这就是 localStorage 的用途(IE 的 userData)

【讨论】:

  • 我宁愿将页面缓存在用户的内存中,也不愿从服务器重新加载!
  • 如果您的配置正确,页面请求应该给出 304 Not Modified 响应。所以浏览器从缓存中读取页面。
  • 到目前为止还不错,但这对我来说意味着额外的流量。我的想法是在开发本地缓存时,我可以减少服务器请求。
  • 构建一个系统来缓存重复数据请求的结果。因此,如果页面两次请求同一个对象,它不会发送第二个 ajax 请求,而是返回缓存的结果。当然,就像任何缓存一样,您必须记住使结果过期,否则您可能会遇到一些奇怪的错误。
  • 我已经有了,但如果我重新加载它。这里的答案旨在在按下后退按钮时重新加载。
【解决方案2】:

除非您已通过跟踪事件的 API 添加事件(如 jQuery 所做的;请参阅 http://api.jquery.com/clone#true ),否则您将无法反映已添加的事件以使其序列化/保留.

如果您不太可能选择使用 DOM 用户数据,您还需要setUserData() 来序列化任何 DOM 用户数据(或者再次使用 jQuery 之类的库来为您跟踪它)。

【讨论】:

  • 我问自己是否允许在不使用JQuery的情况下复制JQuery代码,以及在哪里可以找到clone()的代码
  • 我不能在我的项目中使用 jQuery。
  • 你的意思是,你想知道 jQuery 的哪个部分处理克隆?您可以做的基本上是跟踪事件var Event={els: [], evs: [], cb: [], add:function (el, ev, cb) { el.addEventListener(ev, cb, false); this.els.push(el);this.evs.push(ev); this.cbs.push(cb); }}; 然后您可以在Event.evs 等上进行反省以查找已添加的内容,并在克隆时将其添加回来。
【解决方案3】:

如果将所有内容都放在 IFRAME 中并镜像父级中的所有 URL 片段更改,然后对 iframe 进行操作,那会怎样。不过这里可能存在跨站点脚本问题。

否则你做的事情非常困难,因为浏览器缓存——它决定是否从内存加载——而不是你。因此,如果上述方法不是一个选项,那么创建它的唯一方法是通过浏览器扩展,它会侦听选项卡后退按钮事件并采取相应措施。

【讨论】:

    【解决方案4】:

    你看过jQuery history plugin吗?我知道您在另一篇文章中提到 jQuery 不是一个选项,但您可能会模仿他们的方法。

    我不是该主题的专家,但由于不同的浏览器实现,我相信 这不是微不足道的 让跨浏览器工作。

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

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

    【讨论】:

    • 我看不到他们在哪里进行保存和恢复,我是瞎了吗? github.com/tkyk/jquery-history-plugin/raw/master/…
    • 这是利用 内置 浏览器历史记录实现,而不是以某种方式将页面存储在 JS 内存中的更暴力的方法。基本上,您指示浏览器存储它通常不认为值得存储的动态 URL / 页面状态。正确完成后,后退按钮无需额外工作即可工作。 (如您所见,在某些情况下需要使用 iFrames 跨浏览器进行此操作。)我认为将新(动态)位置设置到浏览器历史记录中的代码是 @987654325 @.
    【解决方案5】:

    您想要的实际上是不可能的,如果您使用像 jquery 这样的库来构建您的页面事件(如 brettz9 建议的那样),您可以做到这一点,但在您的情况下,您使用的是谷歌地图和其他外部库,所以它不是一个选项. 去这里的方法是知道你在哪个页面,并执行初始化那个页面的javascript,就好像它是第一次加载页面一样。为此,您不仅需要跟踪页面的 html,还需要跟踪页面加载时执行的 javascript。

    【讨论】:

      【解决方案6】:

      我知道的唯一方法是欺骗浏览器,让他们相信每个有价值的事件都是一个新页面。您可以通过使用 window.location 加载新页面或使用 get (=query string) 提交表单来做到这一点。新位置或表单数据必须包含显示正确信息所需的一切。这有它的缺点 - 这是我的观点:

      好:

      • 后退和前进按钮有效
      • 可以为您的数据视图添加书签
      • 仔细使用查询字符串数据,它为您的系统提供了一个 API

      不好:

      • 每一个有价值的活动都必须通过页面上传
      • 无法利用 ajax
      • 您必须设计一种方法以查询字符串形式对系统的每个状态进行编码。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-12
        • 2021-11-07
        • 1970-01-01
        • 2016-05-01
        • 2017-01-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多