121

I'm looking for javascript libraries and code that can simulate localStorage on browsers that do not have native support.

Basically, I'd like to code my site using localStorage to store data and know that it will still work on browsers that don't natively support it. This would mean a library would detect if window.localStorage exists and use it if it does. If it doesn't exist, then it would create some sort of fallback method of local storage, by creating its own implementation in the window.localStorage namespace.

So far, I've found these solutions:

  1. Simple sessionStorage implementation.
  2. An implementation that uses cookies (not thrilled with this idea).
  3. Dojo's dojox.storage, but it is it's own thing, not really a fallback.

I understand that Flash and Silverlight can be used for local storage as well, but haven't found anything on using them as a fallback for standard HTML5 localStorage. Perhaps Google Gears has this capability too?

Please share any related libraries, resources, or code snippets that you've found! I'd be especially interested in pure javascript or jquery-based solutions, but am guessing that is unlikely.

Tauren
  • 25,387
  • 37
  • 126
  • 165
  • sessionStorage and localStorage is both part of the Web Storage spec (http://dev.w3.org/html5/webstorage/). Only difference is how long the browser will keep the data. I guess you won't find an implementation where you have one but not the other (but I'm not 100% sure) – rlovtang Jan 14 '11 at 18:26
  • 1
    It's worth mentioning that Gears was [officially depriciated last February](http://gearsblog.blogspot.com/2010/02/hello-html5.html) -- I wouldn't build anything on it. – josh3736 Jan 14 '11 at 20:36
  • @rlovtang: thanks, I am aware of the difference between session and local storage. According to the 24ways.org article (first link in question, solution #1), Chrome only supports localStorage and not sessionStorage. Perhaps that's no longer the case, as that article was written a while ago. – Tauren Jan 15 '11 at 01:00
  • @josh3736: yeah, I'd personally like to avoid using cookies and gears. I certainly wouldn't build anything reliant on it, but if it was a fallback storage mechanism for someone who had it installed, and I didn't code directly to it, it could be used. – Tauren Jan 15 '11 at 01:01
  • so I was actually wrong :) Didn't know Chrome once had support for localStorage but not sessionStorage. Chrome has support for both now at least. – rlovtang Jan 15 '11 at 09:17
  • Just a note here that in my tests with Firefox 15 and 16 if you disable cookies you also disable localStorage. You can't even create a polyfill for it as whenever you try to access `window.localStorage` you get `Error: The operation is insecure.` Throwing a try catch will let you check to see if it's disabled but won't let you replace the variable with your own solution. :/ – Mauvis Ledford Oct 09 '12 at 17:29
  • Thanks for posting this. Are you aware if `sessionStorage` is supported in IE 7 and 8? From my tests, it appears not to be - but I'm unsure. – Kyle Vassella Dec 13 '17 at 16:25

9 Answers9

62

Pure JS based simple localStorage polyfill:

Demo: http://jsfiddle.net/aamir/S4X35/

HTML:

<a href='#' onclick="store.set('foo','bar')">set key: foo, with value: bar</a><br/>
<a href='#' onclick="alert(store.get('foo'))">get key: foo</a><br/>
<a href='#' onclick="store.del('foo')">delete key: foo</a>​

JS:

window.store = {
    localStoreSupport: function() {
        try {
            return 'localStorage' in window && window['localStorage'] !== null;
        } catch (e) {
            return false;
        }
    },
    set: function(name,value,days) {
        if (days) {
            var date = new Date();
            date.setTime(date.getTime()+(days*24*60*60*1000));
            var expires = "; expires="+date.toGMTString();
        }
        else {
            var expires = "";
        }
        if( this.localStoreSupport() ) {
            localStorage.setItem(name, value);
        }
        else {
            document.cookie = name+"="+value+expires+"; path=/";
        }
    },
    get: function(name) {
        if( this.localStoreSupport() ) {
            var ret = localStorage.getItem(name);
            //console.log(typeof ret);
            switch (ret) {
              case 'true': 
                  return true;
              case 'false':
                  return false;
              default:
                  return ret;
            }
        }
        else {
            // cookie fallback
            /*
             * after adding a cookie like
             * >> document.cookie = "bar=test; expires=Thu, 14 Jun 2018 13:05:38 GMT; path=/"
             * the value of document.cookie may look like
             * >> "foo=value; bar=test"
             */
            var nameEQ = name + "=";  // what we are looking for
            var ca = document.cookie.split(';');  // split into separate cookies
            for(var i=0;i < ca.length;i++) {
                var c = ca[i];  // the current cookie
                while (c.charAt(0)==' ') c = c.substring(1,c.length);  // remove leading spaces
                if (c.indexOf(nameEQ) == 0) {  // if it is the searched cookie
                    var ret = c.substring(nameEQ.length,c.length);
                    // making "true" and "false" a boolean again.
                    switch (ret) {
                      case 'true':
                          return true;
                      case 'false':
                          return false;
                      default:
                          return ret;
                    }
                }
            }
            return null; // no cookie found
        }
    },
    del: function(name) {
        if( this.localStoreSupport() ) {
            localStorage.removeItem(name);
        }
        else {
            this.set(name,"",-1);
        }
    }
}​
luckydonald
  • 3,930
  • 2
  • 27
  • 46
Aamir Afridi
  • 6,095
  • 3
  • 38
  • 41
  • 4
    Why is this not getting recognition!? Thanks man! – Robert Jul 30 '13 at 18:42
  • 4
    :) - I don't like adding an entire lib for everything I need. – Aamir Afridi Sep 09 '13 at 16:23
  • 2
    is it allowed in JavaScript to define `var expires` locally and then user in other scope? in function `set` – happy_marmoset Nov 12 '13 at 13:44
  • @happy_marmoset you can try window.store = { expires: 1000,.... and than in set you can use this.expires. Something like that :P – Aamir Afridi Jan 23 '14 at 16:40
  • May want to scope the "ret" variable so it doesn't pollute the global namespace. – Steven May 23 '14 at 21:00
  • 3
    Just wanted to point out that while you're allowing an expiration to be set on the cookie, the localStorage will never expire. This could cause some issues if you are looking for something to expire. It may be beneficial to include adding an expiration date in the localstorage and then making date comparisons to see if it's still applicable. Of course I would also argue that you should just use a cookie if you need expiration. But I think this script should either not allow expiration or allow it, for both, rather than be inconsistent like it currently is. – Hanna Dec 09 '14 at 17:31
  • @Johannes you are right. I will have a look when I have time :) – Aamir Afridi Jan 08 '15 at 10:30
  • @AamirAfrid above provided link http://jsfiddle.net/aamir/S4X35/ is not working on iOS safari (iOS 8) when browsing in private mode, where localstorage is not available actually. – Mohsin Saeed Jan 21 '15 at 09:49
  • 1
    @MohsinSaeed in private mode, I think browser will not allow you to create cookies too. http://superuser.com/questions/589248/can-chrome-incognito-mode-access-regular-chrome-cookies-etc – Aamir Afridi Jan 21 '15 at 15:07
  • 1
    Thanks for sharing this. Just had to make a minor addition of "var" before "ret = localStorage..." to make it work with "use strict"; – adjwilli Sep 06 '16 at 17:05
  • Cookies can be created in Private Browsing mode, it's just that they will be destroyed once the Private Browsing session has ended, regardless of their expiry date. – Gers Jun 13 '17 at 14:47
  • There's a potential issue with the cookie fallback. If the size of the value you want to store exceed the limit of the cookie, only part of the value will be stored. The next time when you use it, say parse using JSON.parse will give an exception. – 牛さん Jun 17 '17 at 03:11
  • Very unsuitable for intense ajax apps. If you set huge object in cookie, it will be exchanged between server and client on each request. And another thing, despite IE11 storage support flag will be ok, the IE might require extra permissions to be set in order to use storage. So window.storage does no good in IE 11. – Mitja Gustin Nov 03 '17 at 12:23
  • This gives SyntaxError: Unexpected identifier 'â' in Safari – Cybernetic Nov 19 '17 at 17:59
  • What's the reason for not using JSON for the cookie storage but manually checking for true/false? – luckydonald Jul 02 '18 at 08:42
56

I use PersistJS (github repository), which handles client-side storage seamlessly and transparently to your code. You use a single API and get support for the following backends:

  • flash: Flash 8 persistent storage.
  • gears: Google Gears-based persistent storage.
  • localstorage: HTML5 draft storage.
  • whatwg_db: HTML5 draft database storage.
  • globalstorage: HTML5 draft storage (old spec).
  • ie: Internet Explorer userdata behaviors.
  • cookie: Cookie-based persistent storage.

Any of those can be disabled—if, for example, you don't want to use cookies. With this library, you'll get native client-side storage support in IE 5.5+, Firefox 2.0+, Safari 3.1+, and Chrome; and plugin-assisted support if the browser has Flash or Gears. If you enable cookies, it will work in everything (but will be limited to 4 kB).

j0k
  • 21,914
  • 28
  • 75
  • 84
josh3736
  • 124,335
  • 26
  • 203
  • 248
  • 10
    Is PersistJS still supported? I'm wondering how it solves a problem where the browser gets upgraded and the chosen storage method changes (say local storage becomes available). Does the old location become in-accessible? – jcalfee314 Feb 20 '14 at 15:35
  • 2
    It at least doesn't look like it's in active development anymore. – Mahn Dec 24 '14 at 13:28
  • This is a very daring library. Without any knowledge about the max-size in the most technologies we should be statisfied that our app runs with much luck... Also this seems to be only a solution if you want to save < 64KB. – dude Jul 21 '15 at 11:14
  • @julmot That's what a library is for. Provide convenience while abstracting away the tedious stuff. With enough research and time, you can usually figure out how to make it work, regardless of max-size. Of course, it looks like the author of that project decided it was too much... – William Jan 20 '16 at 23:43
  • Do you know if this works with Safari Private Browsing mode? Currently I'm having an issue where localStorage is not supported in Safari Private Browsing both macOS and iOS. – Austin Sep 12 '16 at 20:39
  • This, unfortunately, is [by design in Safari](http://stackoverflow.com/q/14555347/201952). – josh3736 Sep 12 '16 at 21:30
  • Bear in mind Flash is getting dropped by pretty much every major browser vendor these days and likely is not a viable option anymore – Liam Nov 10 '16 at 16:23
  • @Liam: anything dropping flash does support `localStorage`, so I don't think that's really an issue. Remember this is a *fallback* library for old browsers. – josh3736 Nov 10 '16 at 16:43
  • Good point.... :) – Liam Nov 10 '16 at 16:48
19

have you seen the polyfill page on the Modernizr wiki?

https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills

look for the webstorage section on that page and you will see 10 potential solutions (as of July 2011).

good luck! Mark

Mark Lummus
  • 740
  • 6
  • 17
  • 1
    It seems that none of them is working in the Private/Incognito mode (e.g. Safari or Chrome), as their workaround is creating cookies, which is also disabled in that mode. – Alex Klaus Jan 10 '17 at 01:00
14

Below is a tidied up version of Aamir Afridi's response that keeps all its code encapsulated within the local scope.

I've removed references that create a global ret variable and also removed the parsing of stored "true" and "false" strings into boolean values within the BrowserStorage.get() method, which could cause issues if one is trying to in fact store the strings "true" or "false".

Since the local storage API only supports string values, one could still store/retrieve JavaScript variable data along with their appropriate data types by encoding said data into a JSON string, which can then be decoded using a JSON encode/decode library such as https://github.com/douglascrockford/JSON-js

var BrowserStorage = (function() {
    /**
     * Whether the current browser supports local storage as a way of storing data
     * @var {Boolean}
     */
    var _hasLocalStorageSupport = (function() {
        try {
            return 'localStorage' in window && window['localStorage'] !== null;
        } catch (e) {
            return false;
        }
    })();

    /**
     * @param {String} name The name of the property to read from this document's cookies
     * @return {?String} The specified cookie property's value (or null if it has not been set)
     */
    var _readCookie = function(name) {
        var nameEQ = name + "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') c = c.substring(1, c.length);
            if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
        }

        return null;
    };

    /**
     * @param {String} name The name of the property to set by writing to a cookie
     * @param {String} value The value to use when setting the specified property
     * @param {int} [days] The number of days until the storage of this item expires
     */
    var _writeCookie = function(name, value, days) {
        var expiration = (function() {
            if (days) {
                var date = new Date();
                date.setTime(date.getTime() + (days*24*60*60*1000));
                return "; expires=" + date.toGMTString();
            }
            else {
                return "";
            }
        })();

        document.cookie = name + "=" + value + expiration + "; path=/";
    };

    return {
        /**
         * @param {String} name The name of the property to set
         * @param {String} value The value to use when setting the specified property
         * @param {int} [days] The number of days until the storage of this item expires (if storage of the provided item must fallback to using cookies)
         */
        set: function(name, value, days) {
            _hasLocalStorageSupport
                ? localStorage.setItem(name, value)
                : _writeCookie(name, value, days);
        },

        /**
         * @param {String} name The name of the value to retrieve
         * @return {?String} The value of the 
         */
        get: function(name) {
            return _hasLocalStorageSupport
                ? localStorage.getItem(name) 
                : _readCookie(name);
        },

        /**
         * @param {String} name The name of the value to delete/remove from storage
         */
        remove: function(name) {
            _hasLocalStorageSupport
                ? localStorage.removeItem(name)
                : this.set(name, "", -1);
        }
    };
})();
Steven
  • 2,381
  • 3
  • 16
  • 26
10

I personally prefer amplify.js. It has worked really well for me in the past and I recommended it for all local storage needs.

supports IE 5+, Firefox 2+, Safari 4+, Chrome, Opera 10.5+, iPhone 2+, Android 2+ and provides a consistent API to handle storage cross-browser

raidfive
  • 6,443
  • 1
  • 32
  • 32
3

store.js uses userData and IE and localStorage on other browsers.

  • It does not try to do anything too complex

  • No cookies, no flash, no jQuery needed.

  • Clean API.

  • 5 kb compressed

https://github.com/marcuswestin/store.js

Mikko Ohtamaa
  • 69,174
  • 40
  • 208
  • 346
1

Lawnchair seems to be a good alternative too

a lawnchair is sorta like a couch except smaller and outside. perfect for html5 mobile apps that need a lightweight, adaptive, simple and elegant persistence solution.

  • collections. a lawnchair instance is really just an array of objects.
  • adaptive persistence. the underlying store is abstracted behind a consistent interface.
  • pluggable collection behavior. sometimes we need collection helpers but not always.
j0k
  • 21,914
  • 28
  • 75
  • 84
1

The MDN page for DOM storage gives several workarounds that use cookies.

alex
  • 438,662
  • 188
  • 837
  • 957
0

There is realstorage, which uses Gears as a fallback.

Gaurav
  • 11,988
  • 2
  • 33
  • 33
  • Thanks. Unfortunately, it looks like it would support fewer browsers than @josh3736's suggestion of PersistJS. I'll still take a look at it, and appreciate the suggestion. – Tauren Jan 15 '11 at 00:56