Skip to content

Instantly share code, notes, and snippets.

@remy
Created March 31, 2010 14:58
Show Gist options
  • Save remy/350433 to your computer and use it in GitHub Desktop.
Save remy/350433 to your computer and use it in GitHub Desktop.
Storage polyfill
if (typeof window.localStorage == 'undefined' || typeof window.sessionStorage == 'undefined') (function () {
var Storage = function (type) {
function createCookie(name, value, days) {
var date, expires;
if (days) {
date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
expires = "; expires="+date.toGMTString();
} else {
expires = "";
}
document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
var nameEQ = name + "=",
ca = document.cookie.split(';'),
i, c;
for (i=0; i < ca.length; i++) {
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;
}
function setData(data) {
data = JSON.stringify(data);
if (type == 'session') {
window.name = data;
} else {
createCookie('localStorage', data, 365);
}
}
function clearData() {
if (type == 'session') {
window.name = '';
} else {
createCookie('localStorage', '', 365);
}
}
function getData() {
var data = type == 'session' ? window.name : readCookie('localStorage');
return data ? JSON.parse(data) : {};
}
// initialise if there's already data
var data = getData();
return {
length: 0,
clear: function () {
data = {};
this.length = 0;
clearData();
},
getItem: function (key) {
return data[key] === undefined ? null : data[key];
},
key: function (i) {
// not perfect, but works
var ctr = 0;
for (var k in data) {
if (ctr == i) return k;
else ctr++;
}
return null;
},
removeItem: function (key) {
delete data[key];
this.length--;
setData(data);
},
setItem: function (key, value) {
data[key] = value+''; // forces the value to a string
this.length++;
setData(data);
}
};
};
if (typeof window.localStorage == 'undefined') window.localStorage = new Storage('local');
if (typeof window.sessionStorage == 'undefined') window.sessionStorage = new Storage('session');
})();
@EmilStenstrom
Copy link

The sessionstorage-exception make it impossible to use localstorage with your snippet. Localstorage does work offline. That's the issue they've fixed.

@aFarkas
Copy link

aFarkas commented May 3, 2011

@remy
He is somehow right. I have also changed the code from

if (typeof window.sessionStorage == 'undefined')

to:

if(!('sessionStorage' in window))

@EmilStenstrom
Copy link

I've done more research, and localStorage does NOT work in IE and Chrome no matter what you do, only Firefox. Read some idea that all local apps could then access other app's content (Like things work on a regular harddrive, what a mess... ;) ).

@ShirtlessKirk
Copy link

That "fix" is simply a silent fail, so the underlying "issue" is not fixed, just ignored.

All storage doesn't work on file:/// because file is not an origin (domain). Not sure how Firefox can get around that apart from silent fail or undeclared permissions elevation. There's no way a web browser should be allowed to access contents of a hard drive outside the scope of the application (i.e. the cache). Even then there shouldn't be the remotest possibility of accessing data unrelated to the current domain.

@EmilStenstrom
Copy link

@ShirtlessKirk: I think Firefox scopes the file access to only files in the same directory. Firefox 2 allowed access to any file, the sub-folder policy was new in Firefox 3. Other browsers allow no access at all.

@nbubna
Copy link

nbubna commented Jun 20, 2011

@remy Your length should only start at zero if the data is empty to begin with. Also, you should only increment the length in setItem if the key doesn't already exist and only decrement the length in removeItem if the key does already exist.

@ricksuggs
Copy link

Just for the sake of convention, shouldn't this script use the === operator as opposed to == ?

Ex/ if (typeof window.localStorage === 'undefined'

@maettig
Copy link

maettig commented Jan 4, 2012

Just to let you know, there is a bug in Firefox, known but not fixed since 2009. A simple typeof window.localStorage causes an exception when executed locally. Please comment and vote at Bugzilla.

@webninjataylor
Copy link

Just a heads up for anyone running into IE7 "JSON undefined" issue after inserting this polyfill. Grab Crockford's JSON polyfill (https://github.com/douglascrockford/JSON-js/blob/master/json2.js) and insert before this one. Nothing like a polyfill to help a polyfill. Hmmm... PP fixes IE. Something poetic there.

@webninjataylor
Copy link

Also note you won't be able to use local storage shorthand with this (localStorage.mydata = 'whatever' ... localStorage.mydata).

@WillsB3
Copy link

WillsB3 commented Feb 8, 2012

Hi Remy - This looks really useful. What licence is it released under?

Many thanks!
Wills

@remy
Copy link
Author

remy commented Feb 8, 2012

@WillsB3
Copy link

WillsB3 commented Feb 8, 2012

Thanks :)

@brettwejrowski
Copy link

Hey Remy, I just came across this and wondered if you thought about implementing this w/o cookies... possibly with globalStorage and userData like this: https://github.com/wojodesign/local-storage-js

@remy
Copy link
Author

remy commented Feb 29, 2012

@brettwejrowski - I'm inclined to totally agree.

@gbakernet
Copy link

@remy, With cookies disabled, merely accessing window.localStorage will throw a Security exception in Firefox (tested FF15). I'm having to add a try/catch in my app to fail gracefully.

@lebbe
Copy link

lebbe commented Nov 23, 2012

In safari with private browsing, using local and session storage gives you:

Error: QUOTA_EXCEEDED_ERR: DOM Exception 22.

Would be nice with a try/catch at the top, so we also can use this in safari with private browsing. :-)

@ghinda
Copy link

ghinda commented Jul 19, 2013

It seems that Opera Mini does not allow double quotes in cookies, that's why the polyfill is not working on it.
http://www.iandevlin.com/blog/2012/04/html5/cookies-json-localstorage-and-opera

I've made a simple fix with two replaces, that turn double quotes into single quotes, and back.
So far it seems to work everywhere, including Opera Mini.
https://gist.github.com/ghinda/6036998

@leachryanb
Copy link

Thanks for this. One comment. You've added JSON.parse and JSON.stringify in the getter and setter. I would pull that for a more accurate representation of the API. The native setters and getters require strings, so it might produce unexpected behavior if the polyfill glosses over this. In the setter, I'm okay with leaving it, since the native would not throw an error anyway, it just does a .toString and stores '[object Object]', but in the getter, you'd have to change your logic between the native and the polyfill.

So:

  function setData(data) {
    data = typeof data !== 'string' ? JSON.stringify(data) : data;
    if (type == 'session') {
      window.name = data;
    } else {
      createCookie('localStorage', data, 365);
    }
  }

  function getData() {
    var data = type == 'session' ? window.name : readCookie('localStorage');
    return data || {};
  }

Thoughts?

@dasilvacontin
Copy link

setItem: function (key, value) {
      data[key] = value+''; // forces the value to a string
      this.length++;
      setData(data);
    }

Shouldn't you check if the key was already set before incrementing length? Like:

setItem: function (key, value) {
      if (data[key] === undefined) this.length++;
      data[key] = value+''; // forces the value to a string
      setData(data);
    }

@vadzappa
Copy link

In Privacy mode in Safari window.localStorage exists while it's not operable. so I think function to check if operable needed:
var isStorageOperable = function (storage) {
try {
storage.setItem("storage", "");
storage.getItem("storage");
storage.removeItem("storage");
return true;
}
catch (err) {
return false;
}
};

and than add check for operable additionally to check for typeof undefined.

@yocontra
Copy link

@AirRider3 @vadzappa I put your fixes in https://gist.github.com/Contra/6368485 - I also applied that length fix to remove (it wasn't checking if the key existed before changing the length)

@hasdavidc
Copy link

@AirRider3 @vadzappa @contra Even with that fix, I'm not sure that it works for private Safari. I think the override of

window.localStorage = new Storage('local');

doesn't do anything because window.localStorage is already implemented as far as private Safari is concerned, and allowing it to be overridden would be a security concern. Please correct me if I'm wrong - I'm still looking for a way to directly use window.localStorage/sessionStorage in private Safari without a data access layer. I tried running Contra's gist without success.

@hasdavidc
Copy link

I think the only workaround for private browsing Safari requires you to overwrite the localStorage/sessionStorage prototypes, as overriding the object itself has no effect.

window.localStorage.__proto__ = new Storage('local');

Here's the complete gist with more complete localStorage support detection: https://gist.github.com/hasdavidc/8527456

@folktrash
Copy link

Best Gist evar?

@maerean
Copy link

maerean commented May 30, 2014

How to detect if the polyfill is really needed (like on iOS in Private mode where the object exists but it throws exceptions (QuotaExceededError: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota) in all methods)

from https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js

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

@yehosef
Copy link

yehosef commented Jul 29, 2014

what about wrapping the function definition in the catch to handle support detection as in https://gist.github.com/yehosef/57df0ec37d5096222cf0

You can add more tests for firefox etc. if desired. I'm dealing specifically with the ios private mode browser.

@jarrodirwin
Copy link

I've combined the ideas from @hasdavidc and @yehosef to account for the non existence of webstorage in older browsers and to accommodate for Safari's private browsing.

I've also tweaked the sessionStorage code so that it acts independent of the localStorage and is unique per window per tab to mimic the native behaviour.

See https://gist.github.com/jarrodirwin/0ce4c0888336b533b2c4

@AdReVice
Copy link

AdReVice commented Jul 6, 2016

hello sir,,
i am quite new in web development..
i just wanted to make a website which can fetch users data and save it locally, using their cookies,..
so how can i do this..
language known by me- c,js,html5,css,jquery

Copy link

ghost commented Jan 6, 2023

Great job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment