As a blogger, one of the things I'm well aware of is how unstable a browser can be. You probably don't think of how often your browser crashes (and to be fair, it's probably been a good month since mine crashed and I run a bleeding edge Chrome install), but if you were ever 500 or so words into a blog entry (or forum posting, or comment) and your browser crashes - you become pretty aware of how nice Word's Auto Save feature is. I first blogged about this back in 2007 (Saving data in case of brower crashes). Back then I used cookies to store a temporary copy of form data while the user was editing. I thought it might be nice to update the idea using HTML 5's Local Storage feature.
HTML Local Storage is one of those darn nice HTML features that doesn't get as much press as it deserves. Unlike it's sexier cousin Canvas, though, Local Storage is actually useful. (Yes, I said it. Canvas is the hot cheerleader in high school. It's getting a lot of attention now but in five years it's going to be pregnant with it's 3rd child and working at Walmart.) I won't go into detail on how it works here (check out the excellent tutorial over at Dive Into HTML5).
The minimum you need to know that is you can treat the local storage feature like a persistent scope within JavaScript. Setting localStorage[somekey] = somevlaue is enough to store the value on the browser. There's size limits of course but for our values (strings) we won't come close to the limit. Best of all, we won't run into the "Evil Too Many Cookies" error. I once spent 2 days diagnosing an issue on a site that came down to the server simply setting one too many cookies. Did the browser bother to tell me? Of course not. It just silently ignored the cookie. (Which to be fair is fine for 99% of the world out there, but for those of who develop web sites, that type of warning would be useful. Unlike Canvas. Ok I'll stop now.)
My basic plan of attack will be this:
- When the browser loads, look for values in the local storage object and restore them.
- Every N seconds, store a copy.
- When the form posts, clear the local storage. (We assume nothing goes wrong server side. Maybe not a safe assumption, but I'm going with it for now.)
Before we do anything, we need to ensure our browser supports local storage. For that, I'm taking a nice function right from the Dive into HTML site:
//Credit: http://diveintohtml5.org/storage.html
function supports_html5_storage() {
try {
return 'localStorage' in window && window['localStorage'] !== null;
} catch (e) {
return false;
}
}
The first thing we do is check for existing values. This will be done when the page loads so I've got it inside my $(document) block:
$(document).ready(function() {
if (supports_html5_storage()) {
//load in old data
loadOld();
That's the loading snipping, and here's loadOld (which is a horrible name, sorry):
function loadOld() {
console.log("Running loadOld");
if(localStorage["blogentry.title"]) $("#title").val(localStorage["blogentry.title"]);
if(localStorage["blogentry.abstract"]) $("#abstract").val(localStorage["blogentry.abstract"]);
if(localStorage["blogentry.body"]) $("#body").val(localStorage["blogentry.body"]);
if(localStorage["blogentry.category"]) $("#category").val(localStorage["blogentry.category"]);
}
Each of these lines are exactly the same except for the key and DOM item they use. Notice that I'm using keys named blogentry.something. Local Storage is unique per site and my blog may be only one small part of a larger application. Using "title" would be too vague so I used a more verbose, but potentially safer naming system.
Next we need to set up our code to store the data. I've got an interval defined first:
window.setInterval(saveData, 5 * 1000);
That 5 second duration there is pretty arbitrary. I type pretty quickly so 5 seconds seems like a reasonable interval. Now for the actual function:
function saveData(){
localStorage["blogentry.title"] = $("#title").val();
localStorage["blogentry.abstract"] = $("#abstract").val();
localStorage["blogentry.body"] = $("#body").val();
localStorage["blogentry.category"] = $("#category").val();
}
Really complex, right? I love how simple this feature is! The final part is handling form submission. We want to clear out the values:
$("#blogEntry").submit(function() {
//could do validation here as well
localStorage["blogentry.title"] = "";
localStorage["blogentry.abstract"] = "";
localStorage["blogentry.body"] = "";
localStorage["blogentry.category"] = "";
});
Notice I'm not actually removing the value but setting it to an empty string. You can also use removeItem(key) to actually delete the entire value. An example of that would be:
localStorage.removeItem("blogentry.title");
As I expect folks to be running this code again and again, it seems ok to keep the key there with an empty value. Ok, so let's not put it all together.
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
//Credit: http://diveintohtml5.org/storage.html
function supports_html5_storage() {
try {
return 'localStorage' in window && window['localStorage'] !== null;
} catch (e) {
return false;
}
} function loadOld() {
console.log("Running loadOld");
if(localStorage["blogentry.title"]) $("#title").val(localStorage["blogentry.title"]);
if(localStorage["blogentry.abstract"]) $("#abstract").val(localStorage["blogentry.abstract"]);
if(localStorage["blogentry.body"]) $("#body").val(localStorage["blogentry.body"]);
if(localStorage["blogentry.category"]) $("#category").val(localStorage["blogentry.category"]);
} function saveData(){
console.log("Running saveData");
localStorage["blogentry.title"] = $("#title").val();
localStorage["blogentry.abstract"] = $("#abstract").val();
localStorage["blogentry.body"] = $("#body").val();
localStorage["blogentry.category"] = $("#category").val();
} $(document).ready(function() {
console.log('init');
if (supports_html5_storage()) {
//load in old data
loadOld();
//start storing
window.setInterval(saveData, 5 * 1000);
//handle the form
$("#blogEntry").submit(function() {
//could do validation here as well
localStorage["blogentry.title"] = "";
localStorage["blogentry.abstract"] = "";
localStorage["blogentry.body"] = "";
localStorage["blogentry.category"] = "";
});
} });
</script>
<style>
fieldset {
width: 400px;
margin-bottom: 10px;
}
input[type=text] {
width: 100%;
}
textarea {
width: 100%;
height: 50px;
}
</style>
</head>
<body> <form id="blogEntry" method="post"> <fieldset>
<label for="title">Title</label>
<input type="text" name="title" id="title">
</fieldset> <fieldset>
<label for="abstract">Abstract</label>
<textarea name="abstract" id="abstract"></textarea>
</fieldset> <fieldset>
<label for="body">Body</label>
<textarea name="body" id="body"></textarea>
</fieldset> <fieldset>
<label for="category">Category</label>
<select name="category" id="category">
<option value="1">ColdFusion</option>
<option value="2">jQuery</option>
<option value="3">Flex</option>
<option value="4">Star Wars</option>
</select>
</fieldset> <input type="submit"> </form> </body>
</html>
<html>
Check out the demo of this here. And before you ask - yes - Local Storage does work in IE. However I used console messages while testing. If you want to try this on IE, just copy and paste the code and remove the console statements.