Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
This is the best localStorage polyfill in the world
// I mean, seriously, localStorage is supported even by your mum. How about instead of
// casing the feature out, you give users in-memory (stale) storage instead?
// If they close your application, they deserve to lose data anyway.
// if (!('localStorage' in window)) {
if (!Modernizr.localstorage) {
window.localStorage = {
_data : {},
setItem : function(id, val) { return this._data[id] = String(val); },
getItem : function(id) { return this._data.hasOwnProperty(id) ? this._data[id] : undefined; },
removeItem : function(id) { return delete this._data[id]; },
clear : function() { return this._data = {}; }
};
}
@lachlanhardy

This comment has been minimized.

Copy link

commented Apr 18, 2011

Modernizr, really?

@juliocesar

This comment has been minimized.

Copy link
Owner Author

commented Apr 18, 2011

Really. But hey, comment that line out, uncomment the previous one, and you're set!

@lachlanhardy

This comment has been minimized.

Copy link

commented Apr 18, 2011

Yeah, I know. I was just kind of surprised you use it.

@juliocesar

This comment has been minimized.

Copy link
Owner Author

commented Apr 18, 2011

I use it quite a lot with yepnope.js to load polyfills only when they're needed.

@DmitryBaranovskiy

This comment has been minimized.

Copy link

commented Apr 18, 2011

Not so fast, cowboy, I would recommend something like this:

(function (isStorage) {
    if (!isStorage) {
        var data = {},
            undef;
        window.localStorage = {
            setItem     : function(id, val) { return data[id] = String(val); },
            getItem     : function(id) { return data.hasOwnProperty(id) ? data[id] : undef; },
            removeItem  : function(id) { return delete data[id]; },
            clear       : function() { return data = {}; }
        };
    }
})((function () {
    try {
        return "localStorage" in window && window.localStorage != null;
    } catch (e) {
        return false;
    }
})());
@juliocesar

This comment has been minimized.

Copy link
Owner Author

commented Apr 18, 2011

Yeah but it's so huge... (that's what she said).

Won't data[id] in getItem always be returning undefined if it doesn't exist anyway?

And yeah, you're right, I should be stringifying before adding. Thanks! Fixed that.

@timoxley

This comment has been minimized.

Copy link

commented Apr 18, 2011

Is there a good reason we don't store this stuff in cookies anymore? eg on write, save localstorage json to cookie, load cookie back in on bootup?

@DmitryBaranovskiy

This comment has been minimized.

Copy link

commented Apr 18, 2011

@juliocesar

localStorage.getItem("toString");
localStorage.getItem("valueOf");
localStorage.getItem("constructor");

Should I continue? ;)

@juliocesar

This comment has been minimized.

Copy link
Owner Author

commented Apr 18, 2011

Yeah, a few reasons. Off the top of my head:

  • Cookies go up to 2kb. localStorage goes way further than that.
  • Clearing cookies as a way to clear a session is not uncommon. Application data can still persist through that.

BTW, there are polyfills for transparent cookie storage / localStorage around.

@juliocesar

This comment has been minimized.

Copy link
Owner Author

commented Apr 18, 2011

@dmitry No :) Thanks a lot. Always learning from you.

@timoxley

This comment has been minimized.

Copy link

commented Apr 18, 2011

Sorry I should have said "in the absence of localstorage" why we don't we fall back on using cookies in polyfills anymore? Will look around.

@juliocesar

This comment has been minimized.

Copy link
Owner Author

commented Apr 18, 2011

This is actually a cool problem to solve... turns out localStorage lets you set items via a [] syntax, as in

localStorage['foo'] = 'bar';

And yes, you can overwrite it's methods too (tested on FF4 and Chrome latest)

localStorage['setItem'] = '44';
localStorage.setItem('test', 'test'); // throws an error

I'll look into mirroring all that later.

@webninjataylor

This comment has been minimized.

Copy link

commented Feb 3, 2012

I wish this was an easy fix for having localStorage in IE7, but the fact is this doesn't save information between page views within the same domain. Also, it doesn't allow for the awesome shorthand for set and get (localStorage.mydata = 'whatever'; ... and ... localStorage.mydata;). So, I think this is the angle @timoxley was coming from.

@timoxley

This comment has been minimized.

Copy link

commented Feb 3, 2012

@webninjataylor spot on.

@juliocesar

This comment has been minimized.

Copy link
Owner Author

commented Feb 4, 2012

Well, of course, as it's in-memory. That's described in the gist up there.

@webninjataylor

This comment has been minimized.

Copy link

commented Feb 4, 2012

@juliocesar, I guess I missed that. Well if it helps (and I know @timoxley will like this) a clean set of polyfills to get persistent local storage in IE7 is to grab this (https://github.com/douglascrockford/JSON-js/blob/master/json2.js) for the JSON polyfill, and then this (https://gist.github.com/350433) for the storage (in that order). Keep in mind 'getItem' and 'setItem' need to be used with this rather than the shorthand. Hope this is useful.

@timoxley

This comment has been minimized.

Copy link

commented Feb 4, 2012

@webninjataylor nice one. good find.

@juliocesar

This comment has been minimized.

Copy link
Owner Author

commented Feb 4, 2012

Thanks! That's most helpful.

@rolandjitsu

This comment has been minimized.

Copy link

commented Feb 5, 2013

The above isn't a polyfill, this is what I would call a polyfill : https://gist.github.com/remy/350433 :)

@benlesh

This comment has been minimized.

Copy link

commented Feb 19, 2013

I have a beef with the polyfill mentioned twice in the comments (https://gist.github.com/remy/350433) It should preferably fallback to other forms of storage if possible (sqlite, userData, etc) before cookies. Cookies are really meant to be used as something to send back and forth to the server. localStorage is for the client only.

@carsonkahn-outdated-account

This comment has been minimized.

Copy link

commented Mar 3, 2013

Change String(val) to JSON.stringify(val), because vanilla objects’ toString casts to "[object Object]". Then again, you might have to pollyfill JSON, which defeats the purpose…

@andrewchilds

This comment has been minimized.

Copy link

commented Feb 27, 2014

This would at least allow for direct setting and getting (and overwriting of localStorage methods, but as mentioned you can do that with the native implementation):

(function () {
  function isSupported() {
    try {
        return 'localStorage' in window && window['localStorage'] !== null;
    } catch(e) {
        return false;
    }
  }

  if (!isSupported()) {
    function init(undef) {
      var store = {
        setItem: function (id, val) {
          return store[id] = String(val);
        },
        getItem: function (id) {
          return store.hasOwnProperty(id) ? String(store[id]) : undef;
        },
        removeItem: function (id) {
          return delete store[id];
        },
        clear: function () {
          init();
        }
      };

      window.localStorage = store;
    }
    init();
  }
}());

@carsonkahn, the native implementation does that too, at least in Chrome:

window.localStorage.setItem('foo', { a: 1, b: 2 });
// undefined
window.localStorage.getItem('foo');
// "[object Object]"
@m93a

This comment has been minimized.

Copy link

commented Feb 22, 2015

Garbage.
The point is to keep data across refreshes.
That's why people use cookies in polyfills.
See the spec or MDN.

@Download

This comment has been minimized.

Copy link

commented Aug 24, 2015

Not really. Currently all browsers that are still relevant already support local storage. The only reason for the feature detect and fallback to memory use today is Safari and it's Private Browsing Mode. And when Private Browsing Mode is enabled, all hope of storing something longer-term is lost anyway...

The feature detect must perform a setItem call wrapped in a try...catch to be useful. I recommend also testing if you get back what you stored as it's just one extra line. Then, if it's not available, you either wrap all calls to localStorage in if blocks, or you use some shim instead.

Here is how I deal with it in my code:

var DB;
try {
    var x = 'test_localstorage_available_' + Date.now();
    localStorage.setItem(x, x);
    var y = localStorage.getItem(x);
    localStorage.removeItem(x);
    if (x !== y) {throw new Error();}
    DB = localStorage;
}
catch(e) {
    DB = new MemoryStorage('my-cool-app');
}

memorystorage is a little library I wrote that implements functional storage in memory that follows the Web Storage API. For more information check out my blog post on this topic: Introducing MemoryStorage.

@bleuscyther

This comment has been minimized.

Copy link

commented Nov 13, 2015

@Download is correct, !Modernizr.localstorage returns true in Private Browsing Mode for Safari even if you can't use it.

However,if the user is using Private Browsing you can in most case use a combination of what you said and this polyfill.

// fix issue where localStorage or sessionStorage are not available , example  : incognito Mode
    try
    {
         window.sessionStorage.setItem('testKey', '1');
         window.sessionStorage.removeItem('testKey');
    }
    catch (error)
    {
         window.sessionStorage = {
            _data       : {},
            setItem     : function(id, val) { return this._data[id] = String(val); },
            getItem     : function(id) { return this._data.hasOwnProperty(id) ? this._data[id] : undefined; },
            removeItem  : function(id) { return delete this._data[id]; },
            clear       : function() { return this._data = {}; }
          };
    }

Same thing for LocalStorage

@bryanjhv

This comment has been minimized.

Copy link

commented Nov 14, 2015

And what would the difference be if we replace in getItem:

return this._data.hasOwnProperty(id) // ...

With

return !(id in {}) // ...
@morinted

This comment has been minimized.

Copy link

commented Jan 5, 2016

Here's what I used with ES2015 arrow functions to let me "use" local storage while testing:

const ls = typeof localStorage !== 'undefined' && localStorage ?
  localStorage :
  { _data: {}
  , setItem: (id, val) => ls._data[id] = String(val)
  , getItem: id => ls._data.hasOwnProperty(id) ? ls._data[id] : undefined
  , removeItem: id => delete ls._data[id]
  , clear: () => ls._data = {}
  }

I then used ls locally instead of localStorage

@iammerrick

This comment has been minimized.

Copy link

commented Jan 7, 2016

@morinted I don't think that supports the case when someone requests a property from localStorage directly e.g. localStorage.isItemThere

@ArnaudValensi

This comment has been minimized.

Copy link

commented Apr 26, 2016

On the some recent browsers, like safari on iOS, window.localStorage is not assignable, but Storage object is, which affects localStorage functions.

Here is my version of the pollyfill:

(function () {
  function isSupported() {
    var item = 'localStoragePollyfill';
    try {
      localStorage.setItem(item, item);
      localStorage.removeItem(item);
      return true;
    } catch (e) {
      return false;
    }
  }

  if (!isSupported()) {
    try {
      Storage.prototype._data = {};

      Storage.prototype.setItem = function (id, val) {
        return this._data[id] = String(val);
      };

      Storage.prototype.getItem = function (id) {
        return this._data.hasOwnProperty(id) ? this._data[id] : undefined;
      },

      Storage.prototype.removeItem = function (id) {
        return delete this._data[id];
      },

      Storage.prototype.clear = function () {
        return this._data = {};
      }
    } catch (e) {
      console.error('localStorage pollyfill error: ', e);
    }
  }
}());
@zim32

This comment has been minimized.

Copy link

commented Jun 4, 2016

This is not a replacement of localstorage, because localstorage is persistent across page reloads

@johny-gog

This comment has been minimized.

Copy link

commented Jun 20, 2016

Shouldn't it return null if key is not there?

@artemdemo

This comment has been minimized.

Copy link

commented Jul 20, 2016

@johny-gog is right.
It should be null instead of undefined when there is no id.

@luobotang

This comment has been minimized.

Copy link

commented Aug 25, 2016

not a good way.

@enten

This comment has been minimized.

Copy link

commented Dec 18, 2016

@ArnaudValensi

I don't understand how window.localStorage can be nil if the window.Storage interface exists.
What is the versions of Safari and iOS with which you encountered this issue?

Please tag me on your response sir.

@asfktz

This comment has been minimized.

Copy link

commented Jan 2, 2017

Shouldn't it return null if key is not there?

Thanks for pointing on it, it solve a bug for me.
the case of: JSON.parse(localStorage.getItem('foo'))

@digital-flowers

This comment has been minimized.

Copy link

commented May 8, 2017

bfff you need to check this polyfill:

if (!window.localStorage) {
  window.localStorage = {
    getItem: function (sKey) {
      if (!sKey || !this.hasOwnProperty(sKey)) { return null; }
      return unescape(document.cookie.replace(new RegExp("(?:^|.*;\\s*)" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*"), "$1"));
    },
    key: function (nKeyId) {
      return unescape(document.cookie.replace(/\s*\=(?:.(?!;))*$/, "").split(/\s*\=(?:[^;](?!;))*[^;]?;\s*/)[nKeyId]);
    },
    setItem: function (sKey, sValue) {
      if(!sKey) { return; }
      document.cookie = escape(sKey) + "=" + escape(sValue) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
      this.length = document.cookie.match(/\=/g).length;
    },
    length: 0,
    removeItem: function (sKey) {
      if (!sKey || !this.hasOwnProperty(sKey)) { return; }
      document.cookie = escape(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
      this.length--;
    },
    hasOwnProperty: function (sKey) {
      return (new RegExp("(?:^|;\\s*)" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie);
    }
  };
  window.localStorage.length = (document.cookie.match(/\=/g) || window.localStorage).length;
}

source: https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage

@theUtherSide

This comment has been minimized.

Copy link

commented Jul 5, 2017

Looks like this Gist needs a new name :o)

I just want to add, there are newer/better/shinier ways to detect if localStorage is functional.
Many browser scenarios disable the features, even if window.localStorage is defined.

A beautiful complete history and solution here:

https://gist.github.com/paulirish/5558557

try {
    localStorage.setItem('mod', 'mod');
    localStorage.removeItem('mod');
    return true;
} catch(e) {
    return false;
}
@icharge

This comment has been minimized.

Copy link

commented Jun 14, 2018

From @DmitryBaranovskiy 's snippet.
I just edit some. and can use localStorage['foo'] = 'bar';

(function (isStorage) {
  if (!isStorage) {
    function Storage() { };
    Storage.prototype.setItem = function (id, val) {
      return this[id] = JSON.stringify(val);
    }
    Storage.prototype.getItem = function (id) {
      return this.hasOwnProperty(id) ? this[id] : null;
    }
    Storage.prototype.removeItem = function (id) {
      return delete this[id];
    }
    Storage.prototype.clear = function () {
      var self = this;
      Object.keys(this).map(function (key) { self.removeItem(key) });
    }
    window.localStorage = new Storage();
  }
})((function () {
  try {
    return "localStorage" in window && window.localStorage != null;
  } catch (e) {
    return false;
  }
})());

2018-08-02 Updated

  • return null instead of undefined
  • hasOwnProperty must passed id
@odahcam

This comment has been minimized.

Copy link

commented Jul 18, 2018

Just a observation getItem returns null when it doesn't find any value for the key, not undefined. Returning undefined may cause deserialization errors in any JSON.parse expecting a storage value.

@icharge

This comment has been minimized.

Copy link

commented Aug 2, 2018

Updated

@pacotole

This comment has been minimized.

Copy link

commented Oct 23, 2018

If you want the simplicity of @juliocesar gist but keep the data in a cookie:

window.localStorage = {
    _data       : JSON.parse(document.cookie.replace(/(?:(?:^|.*;\s*)localStorage\s*\=\s*([^;]*).*$)|^.*$/, "$1") || '{}'),
    _save       : function() { document.cookie = "localStorage=" + JSON.stringify(this._data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/"; },
    setItem     : function(id, val) { this._data[id] = String(val); this._save(); },
    getItem     : function(id) { return this._data.hasOwnProperty(id) ? this._data[id] : null; },
    removeItem  : function(id) { delete this._data[id]; this._save(); },
    clear       : function() { this._data = {}; this._save(); }
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.