Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
LocalStorage/SessionStorage Polyfill with Safari Private Browsing support.
// Refer to https://gist.github.com/remy/350433
try {
// Test webstorage existence.
if (!window.localStorage || !window.sessionStorage) throw "exception";
// Test webstorage accessibility - Needed for Safari private browsing.
localStorage.setItem('storage_test', 1);
localStorage.removeItem('storage_test');
} catch(e) {
(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); 2480
}
if (c.indexOf(nameEQ) == 0) {
return c.substring(nameEQ.length,c.length);
}
}
return null;
}
function setData(data) {
// Convert data into JSON and encode to accommodate for special characters.
data = encodeURIComponent(JSON.stringify(data));
// Create cookie.
if (type == 'session') {
createCookie(getSessionName(), data, 365);
} else {
createCookie('localStorage', data, 365);
}
}
function clearData() {
if (type == 'session') {
createCookie(getSessionName(), '', 365);
} else {
createCookie('localStorage', '', 365);
}
}
function getData() {
// Get cookie data.
var data = type == 'session' ? readCookie(getSessionName()) : readCookie('localStorage');
// If we have some data decode, parse and return it.
return data ? JSON.parse(decodeURIComponent(data)) : {};
}
function getSessionName() {
// If there is no name for this window, set one.
// To ensure it's unquie use the current timestamp.
if(!window.name) {
window.name = new Date().getTime();
}
return 'sessionStorage' + window.name;
}
// 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);
}
};
};
// Replace window.localStorage and window.sessionStorage with out custom
// implementation.
var localStorage = new Storage('local');
var sessionStorage = new Storage('session');
window.localStorage = localStorage;
window.sessionStorage = sessionStorage;
// For Safari private browsing need to also set the proto value.
window.localStorage.__proto__ = localStorage;
window.sessionStorage.__proto__ = sessionStorage;
})();
}
@haydnhkim

This comment has been minimized.

Copy link

haydnhkim commented Nov 6, 2014

thank you!

@bigotilda

This comment has been minimized.

Copy link

bigotilda commented Sep 9, 2015

Very helpful! I assume the 2480 at the end of line 32 is a typo?

@ibnIrshad

This comment has been minimized.

Copy link

ibnIrshad commented Sep 16, 2015

This doesn't work on Safari 9.0 on OS X El Capitan. I get: Error: Attempted to assign to readonly property.. Seems like they've cracked down on polyfilling, what a shame.

@majuric

This comment has been minimized.

Copy link

majuric commented Oct 15, 2015

The problem seems not to be only with Safari 9.0 but also if the user disables local storage in the browser completely .. in Chrome the following error is thrown:
DOMException: Failed to read the 'localStorage' property from 'Window': Access is denied for this document.

I've made slight modification and wrapped this block in a try - catch:
https://gist.github.com/daang77/fa08e4adc5277c2588f5

@Thinkscape

This comment has been minimized.

Copy link

Thinkscape commented Jan 4, 2016

Warning - this polyfill will fail in many browsers

Even with the try/catch block, localStorage will still be broken in many cases. When it is disabled due to some privacy settings, any attempts to access window.localStorage or setItem() getItem() methods will throw exceptions, regardless of the polyfill.

Here are some notable examples where it breaks:

  • Mobile Chrome as a webview in many apps (i.e. official Twitter App on Android)
  • Chrome and Chrome Mobile with Local storage/cookies disabled (privacy settings).
  • Firefox with localStorage disabled.

The only reasonable workaround is using a wrapper instead of relying on window.localStorage function.

@aemonge

This comment has been minimized.

Copy link

aemonge commented Jan 17, 2017

Works like a charm for me, we only have to remove the typo @bigotilda noticed !

@s-efim

This comment has been minimized.

Copy link

s-efim commented Feb 9, 2017

Nice! This method is working correctly in Safari Private Mode. Thanks

@yavuzKomecoglu

This comment has been minimized.

Copy link

yavuzKomecoglu commented Mar 28, 2017

you rock!,..... you're the man! Thanks.

@orion110217

This comment has been minimized.

Copy link

orion110217 commented Jan 7, 2018

I created a working one based on this.

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.