{"rowid": 233, "title": "Wrapping Things Nicely with HTML5 Local Storage", "contents": "HTML5 is here to turn the web from a web of hacks into a web of applications \u2013 and we are well on the way to this goal. The coming year will be totally and utterly awesome if you are excited about web technologies.\n\nThis year the HTML5 revolution started and there is no stopping it. For the first time all the browser vendors are rallying together to make a technology work. The new browser war is fought over implementation of the HTML5 standard and not over random additions. We live in exciting times.\n\nStarting with a bang\n\nAs with every revolution there is a lot of noise with bangs and explosions, and that\u2019s the stage we\u2019re at right now. HTML5 showcases are often CSS3 showcases, web font playgrounds, or video and canvas examples.\n\nThis is great, as it gets people excited and it gives the media something to show. There is much more to HTML5, though. Let\u2019s take a look at one of the less sexy, but amazingly useful features of HTML5 (it was in the HTML5 specs, but grew at such an alarming rate that it warranted its own spec): storing information on the client-side.\n\nWhy store data on the client-side?\n\nStoring information in people\u2019s browsers affords us a few options that every application should have:\n\n\n\tYou can retain the state of an application \u2013 when the user comes back after closing the browser, everything will be as she left it. That\u2019s how \u2018real\u2019 applications work and this is how the web ones should, too.\n\tYou can cache data \u2013 if something doesn\u2019t change then there is no point in loading it over the Internet if local access is so much faster\n\tYou can store user preferences \u2013 without needing to keep that data on your server at all.\n\n\nIn the past, storing local data wasn\u2019t much fun.\n\nThe pain of hacky browser solutions\n\nIn the past, all we had were cookies. I don\u2019t mean the yummy things you get with your coffee, endorsed by the blue, furry junkie in Sesame Street, but the other, digital ones. Cookies suck \u2013 it isn\u2019t fun to have an unencrypted HTTP overhead on every server request for storing four kilobytes of data in a cryptic format. It was OK for 1994, but really neither an easy nor a beautiful solution for the task of storing data on the client.\n\nThen came a plethora of solutions by different vendors \u2013 from Microsoft\u2019s userdata to Flash\u2019s LSO, and from Silverlight isolated storage to Google\u2019s Gears. If you want to know just how many crazy and convoluted ways there are to store a bit of information, check out Samy\u2019s evercookie.\n\nClearly, we needed an easier and standardised way of storing local data.\n\nKeeping it simple \u2013 local storage\n\nAnd, lo and behold, we have one. The local storage API (or session storage, with the only difference being that session data is lost when the window is closed) is ridiculously easy to use. All you do is call a few methods on the window.localStorage object \u2013 or even just set the properties directly using the square bracket notation:\n\nif('localStorage' in window && window['localStorage'] !== null){\n\tvar store = window.localStorage;\n\n\t// valid, API way\n\t\tstore.setItem(\u2018cow\u2019,\u2018moo\u2019);\n\t\tconsole.log(\n\t\tstore.getItem(\u2018cow\u2019)\n\t); // => \u2018moo\u2019\n\n\t// shorthand, breaks at keys with spaces\n\t\tstore.sheep = \u2018baa\u2019 \n\t\tconsole.log(\n\t\tstore.sheep \n\t); // \u2018baa\u2019\n\n\t// shorthand for all\n\t\tstore[\u2018dog\u2019] = \u2018bark\u2019\n\t\tconsole.log(\n\t\tstore[\u2018dog\u2019]\n\t); // => \u2018bark\u2019\n\n}\n\nBrowser support is actually pretty good: Chrome 4+; Firefox 3.5+; IE8+; Opera 10.5+; Safari 4+; plus iPhone 2.0+; and Android 2.0+. That should cover most of your needs. Of course, you should check for support first (or use a wrapper library like YUI Storage Utility or YUI Storage Lite).\n\nThe data is stored on a per domain basis and you can store up to five megabytes of data in localStorage for each domain.\n\nStrings attached\n\nBy default, localStorage only supports strings as storage formats. You can\u2019t store results of JavaScript computations that are arrays or objects, and every number is stored as a string. This means that long, floating point numbers eat into the available memory much more quickly than if they were stored as numbers.\n\nvar cowdesc = \"the cow is of the bovine ilk, \"+\n\t\t\"one end is for the moo, the \"+\n\t\t\"other for the milk\";\n\nvar cowdef = {\n\tilk\u201cbovine\u201d,\n\tlegs,\n\tudders,\n\tpurposes\n\t\tfront\u201cmoo\u201d,\n\t\tend\u201cmilk\u201d\n\t}\n};\n\nwindow.localStorage.setItem(\u2018describecow\u2019,cowdesc);\nconsole.log(\n\twindow.localStorage.getItem(\u2018describecow\u2019)\n); // => the cow is of the bovine\u2026 \n\nwindow.localStorage.setItem(\u2018definecow\u2019,cowdef);\nconsole.log(\n\twindow.localStorage.getItem(\u2018definecow\u2019)\n); // => [object Object] = bad!\n\nThis limits what you can store quite heavily, which is why it makes sense to use JSON to encode and decode the data you store:\n\nvar cowdef = {\n\t\"ilk\":\"bovine\",\n\t\"legs\":4,\n\t\"udders\":4,\n\t\"purposes\":{\n\t\"front\":\"moo\",\n\t\"end\":\"milk\"\n\t}\n};\n\nwindow.localStorage.setItem(\u2018describecow\u2019,JSON.stringify(cowdef));\nconsole.log(\n\tJSON.parse(\n\t\twindow.localStorage.getItem(\u2018describecow\u2019)\n\t)\n); // => Object { ilk=\u201cbovine\u201d, more\u2026}\n\nYou can also come up with your own formatting solutions like CSV, or pipe | or tilde ~ separated formats, but JSON is very terse and has native browser support.\n\nSome use case examples\n\nThe simplest use of localStorage is, of course, storing some data: the current state of a game; how far through a multi-form sign-up process a user is; and other things we traditionally stored in cookies. Using JSON, though, we can do cooler things.\n\nSpeeding up web service use and avoiding exceeding the quota\n\nA lot of web services only allow you a certain amount of hits per hour or day, and can be very slow. By using localStorage with a time stamp, you can cache results of web services locally and only access them after a certain time to refresh the data.\n\nI used this technique in my An Event Apart 10K entry, World Info, to only load the massive dataset of all the world information once, and allow for much faster subsequent visits to the site. The following screencast shows the difference:\n\n\n\nFor use with YQL (remember last year\u2019s 24 ways entry?), I\u2019ve built a small script called YQL localcache that wraps localStorage around the YQL data call. An example would be the following:\n\nyqlcache.get({\n\tyql: 'select * from flickr.photos.search where text=\"santa\"',\n\tid: 'myphotos',\n\tcacheage: ( 60*60*1000 ),\n\tcallback: function(data) {\n\t\tconsole.log(data);\n\t}\n});\n\nThis loads photos of Santa from Flickr and stores them for an hour in the key myphotos of localStorage. If you call the function at various times, you receive an object back with the YQL results in a data property and a type property which defines where the data came from \u2013 live is live data, cached means it comes from cache, and freshcache indicates that it was called for the first time and a new cache was primed. The cache will work for an hour (60\u00d760\u00d71,000 milliseconds) and then be refreshed. So, instead of hitting the YQL endpoint over and over again, you hit it once per hour.\n\nCaching a full interface\n\nAnother use case I found was to retain the state of a whole interface of an application by caching the innerHTML once it has been rendered. I use this in the Yahoo Firehose search interface, and you can get the full story about local storage and how it is used in this screencast:\n\n\n\nThe stripped down code is incredibly simple (JavaScript with PHP embed):\n\n// test for localStorage support\nif(('localStorage' in window) && window['localStorage'] !== null){\n\nvar f = document.getElementById(\u2018mainform\u2019);\n// test with PHP if the form was sent (the submit button has the name \u201csent\u201d)\n\n\n\t// get the HTML of the form and cache it in the property \u201cstate\u201d\n\tlocalStorage.setItem(\u2018state\u2019,f.innerHTML);\n\n// if the form hasn\u2019t been sent\u2026\n\n\n\t// check if a state property exists and write back the HTML cache\n\tif(\u2018state\u2019 in localStorage){\n\t\tf.innerHTML = localStorage.getItem(\u2018state\u2019); \n\t}\n\n\t\n\n}\n\nOther ideas\n\nIn essence, you can use local storage every time you need to speed up access. For example, you could store image sprites in base-64 encoded datasets instead of loading them from a server. Or you could store CSS and JavaScript libraries on the client. Anything goes \u2013 have a play.\n\nIssues with local and session storage\n\nOf course, not all is rainbows and unicorns with the localStorage API. There are a few niggles that need ironing out. As with anything, this needs people to use the technology and raise issues. Here are some of the problems:\n\n\n\tInadequate information about storage quota \u2013 if you try to add more content to an already full store, you get a QUOTA_EXCEEDED_ERR and that\u2019s it. There\u2019s a great explanation and test suite for localStorage quota available.\n\tLack of automatically expiring storage \u2013 a feature that cookies came with. Pamela Fox has a solution (also available as a demo and source code)\n\tLack of encrypted storage \u2013 right now, everything is stored in readable strings in the browser.\n\n\nBigger, better, faster, more!\n\nAs cool as the local and session storage APIs are, they are not quite ready for extensive adoption \u2013 the storage limits might get in your way, and if you really want to go to town with accessing, filtering and sorting data, real databases are what you\u2019ll need. And, as we live in a world of client-side development, people are moving from heavy server-side databases like MySQL to NoSQL environments.\n\nOn the web, there is also a lot of work going on, with Ian Hickson of Google proposing the Web SQL database, and Nikunj Mehta, Jonas Sicking (Mozilla), Eliot Graff (Microsoft) and Andrei Popescu (Google) taking the idea beyond simply replicating MySQL and instead offering Indexed DB as an even faster alternative.\n\nOn the mobile front, a really important feature is to be able to store data to use when you are offline (mobile coverage and roaming data plans anybody?) and you can use the Offline Webapps API for that.\n\nAs I mentioned at the beginning, we have a very exciting time ahead \u2013 let\u2019s make this web work faster and more reliably by using what browsers offer us. For more on local storage, check out the chapter on Dive into HTML5.", "year": "2010", "author": "Christian Heilmann", "author_slug": "chrisheilmann", "published": "2010-12-06T00:00:00+00:00", "url": "https://24ways.org/2010/html5-local-storage/", "topic": "code"}