简体   繁体   中英

How to save and restore the whole website using Javascript?

Hello I am developing my own router api in Javascript. It does routing based on #FregmentIdentifiers (document.location.hash).

The api is almos finished but I am still working on the backbuttom event. Whenever the backbuttom is pressed and the hash has changed, and was seen before, the old content will be restored.

Do you know a way to do the saving and restoring of all content?

My problem here is that if I save and restore document.body.innerHTML only the markup is restored but not the events, so eg googlemaps stops working. I was trying to clone document.body or document.documentElement but javascript either told me that the field does not have a setter or that my clone is not valid.

EDIT:

To make clear after all what I am working on, I decided to post my current code. The question aims at the parts marked with a //TODO comment.

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;
    };
}

An example usage of my api can look like this:

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();

After all Javascript files are included (which may register routes) I call Router.getInstance().init();

When I do somewhere an ajax request (by hand) for which a route exists, I call ROUTE_NAME.executed() to set the fregment identifier and to register it with the history.

Furthermore I have an observer which updates some links, which are used for direct translations, whenever a location hash is changed by executed()

This is the same situation as a refresh so you should re-use that system.

Basicly your hash has to contain enough information to rebuild the whole page. Ofcourse, sometimes you need to save some user input to rebuild a page. That's where the localStorage is for (userData for IE)

Unless you've added your events through an API which tracks them (like jQuery does; see http://api.jquery.com/clone#true ), you won't be able to reflect on the events which have been added in order to get them serialized/preserved.

If you have made the unlikely choice of using DOM user data, you will also need setUserData() to serialize any DOM user data (or again, a library like jQuery to track it for you).

What about having everything in an IFRAME and mirror all the URL fragment changes in the parent, then act on the iframe. Could be possible cross site scripting issue here though.

It's very difficult what your doing otherwise because the browser caches -- it decides whether to load from memory -- not you. So if the above isn't an option, the only way you'll create this is via a browser extention, which listens for the tab back button event and act accordingly.

Have you looked at the jQuery history plugin ? I know in another post you mentioned jQuery was not an option, but you might mimic their approach.

I'm not an expert on the subject, but because of the different browser implementations, I believe this is not trivial to get working cross-browser.

Main site: http://tkyk.github.com/jquery-history-plugin/#

Demo page: http://www.serpere.info/jquery-history-plugin/samples/ajax/

What you want is practically impossible, you could do it if you used a library like jquery to build the events of your page like brettz9 suggested, but in your case you are using google maps and other external libraries so it's not an option. The way to go here is to know which page you are on, and execute the javascript that initializes that page as if it was the first time the page is loaded. For that you need to keep track not only the html of the page, but also the javascripts that get executed when the page loads.

The only way I know is to deceive the browser into believing that each worthwhile event is a new page. You could do that by loading a new page using window.location, or submit a form using get (=query string). The new location, or the form data, has to contain everything necessary for displaying the right information. This has its downsides - here's my view:

Good:

  • back and forward buttons work
  • views of your data can be bookmarked
  • with careful use of query string data, it offers an API to your system

Bad:

  • every worthwhile event has to go through a page upload
  • can't take advantage of ajax
  • you have to devise a way to encode every state of your system in query string form.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM