Related
I've written a webapp that allows you to store the images in the localStorage until you hit save (so it works offline, if signal is poor).
When the localStorage reaches 5MB Google Chrome produces an error in the javascript console log:
Uncaught Error: QUOTA_EXCEEDED_ERR: DOM Exception 22
How do I increase the size of the localStorage quota on Google Chrome?
5MB is a hard limit and that is stupid. IndexedDB gives you ~50MB which is more reasonable. To make it easier to use try Dexie.js https://github.com/dfahlander/Dexie.js
Update:
Dexie.js was actually still an overkill for my simple key-value purposes so I wrote this much simpler script https://github.com/DVLP/localStorageDB
with this you have 50MB and can get and set values like that
// Setting values
ldb.set('nameGoesHere', 'value goes here');
// Getting values - callback is required because the data is being retrieved asynchronously:
ldb.get('nameGoesHere', function (value) {
console.log('And the value is', value);
});
Copy/paste the line below so ldb.set() and ldb.get() from the example above will become available.
!function(){function e(t,o){return n?void(n.transaction("s").objectStore("s").get(t).onsuccess=function(e){var t=e.target.result&&e.target.result.v||null;o(t)}):void setTimeout(function(){e(t,o)},100)}var t=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB;if(!t)return void console.error("indexDB not supported");var n,o={k:"",v:""},r=t.open("d2",1);r.onsuccess=function(e){n=this.result},r.onerror=function(e){console.error("indexedDB request error"),console.log(e)},r.onupgradeneeded=function(e){n=null;var t=e.target.result.createObjectStore("s",{keyPath:"k"});t.transaction.oncomplete=function(e){n=e.target.db}},window.ldb={get:e,set:function(e,t){o.k=e,o.v=t,n.transaction("s","readwrite").objectStore("s").put(o)}}}();
You can't, it's hard-wired at 5MB. This is a design decision by the Chrome developers.
In Chrome, the Web SQL db and cache manifest also have low limits by default, but if you package the app for the Chrome App Store you can increase them.
See also Managing HTML5 Offline Storage - Google Chrome.
The quota is for the user to set, how much space he wishes to allow to each website.
Therefore since the purpose is to restrict the web pages, the web pages cannot change the restriction.
If storage is low, you can prompt the user to increase local storage.
To find out if storage is low, you could probe the local storage size by saving an object then deleting it.
You can't but if you save JSON in your localStorage you can use a library to compress data like : https://github.com/k-yak/JJLC
demo : http://k-yak.github.io/JJLC/
Here you can test your program , you should handle also the cases when the cuota is exceed
https://stackoverflow.com/a/5664344/2630686 The above answer is much amazing. I applied it in my project and implement a full solution to request all kinds of resource.
// Firstly reference the above ldb code in the answer I mentioned.
export function get_file({ url, d3, name, enable_request = false }) {
if (name === undefined) { // set saved data name by url parsing alternatively
name = url.split('?')[0].split('/').at(-1).split('.')[0];
}
const html_name = location.href.split('/').at(-1).split('.')[0]
name = `${html_name}_${name}`
let ret = null;
const is_outer = is_outer_net(url); // check outer net url by its start with http or //
// try to access data from local. Return null if not found
if (is_outer && !enable_request) {
if (localStorage[name]) {
ret = new Promise(resolve => resolve(JSON.parse(localStorage[name])));
} else {
ret = new Promise(r => {
ldb.get(name, function (value) {
r(value)
})
});
}
} else {
ret = new Promise(r => r(null))
}
ret.then(data => {
if (data) {
return data
} else {
const method = url.split('.').at(-1)
// d3 method supported
if (d3 && d3[method]) {
ret = d3[method](url)
} else {
if (url.startsWith('~/')) { // local files accessed supported. You need a local service that can return local file data by requested url's address value
url = `http://localhost:8010/get_file?address=${url}`
}
ret = fetch(url).then(data => {
// parse data by requested data type
if (url.endsWith('txt')) {
return data.text()
} else {
return data.json()
}
})
}
ret = ret.then(da => {
data = da
if (is_outer) { // save data to localStorage firstly
localStorage[name] = JSON.stringify(data);
}
}).catch(e => { // save to ldb if 5MB exceed
ldb.set(name, data);
}).finally(_ => {
return data;
});
}
})
return ret;
}
I have 2 problems:
When I load the HTML page, and I look in the "Debug" in inspect it continuously says that the Local Storage is undefined
How to display the Local Storage value in the HTML page
JS code:
var loadLives = setInterval(showLives, 100);
// get lives from localstorage
var livesInStorage = window.localstorage.getItem('lives');
let lives;
if(livesInstorage) {
//parse localstorage data to Int
lives = parseInt(livesInstorage, 10);
}
else {
// initial value (in this case: 3)
lives = 3;
}
function loseLife() {
if (lives > 1) {
lives = --lives;
// set changed value in localStorage
window.localstorage.setItem('lives', lives);
}
else {
location.href='fail.html';
}
}
function showLives() {
document.getElementById("lifeamount").innerHTML = window.localstorage.getItem('lives');
}
From the code you shared I can see that you set the variable 'lives' to 3 as initial value, but you didn't set it in the Local Storage there.
You only set it in the Local Storage when you call 'loseLives', but I don't see a call to this function.
Also, you have a typo. You wrote localstorage instead of localStorage.
The goal is the user can save up to 7 field vals in obj userA, logout, log back in and the saved vals are there, retrievable. Specific to each user.
I am trying to create an object i.e. userA and update it, as the
user saves each field value (i.e. BaseMap: basemapSaved), save
the updated state in local storage, then retrieve saved state using
local storage. So, when the user logs out, then logs back in, their
saved data is still there specific to their username.
Below is my most recent attempt (full js): Any pointers? Am I going about this all wrong?
UPDATED ATTEMPT BELOW WITH BOUNTY.
I am simply trying to save an object of data and a field within it (i.e. userA.BaseMap.basemapSaved;) with local storage, on click.
I later want to parse that saved object in local storage, get that field, and update my API object i.e. object.data.field (userA.BaseMap.basemapSaved;) with the value saved and gathered. I can do this pro grammatically pretty easy, but the idea is to save the state per user, so they can log out, then log back in and have their selection saved.
// Here I am trying to initialize the variables
var currentUser;
var basemapSaved;
var userA[key] = {};
// This function I am getting the logged in username, I want to set this as the object key in userA i.e. userA[key]
function checkUser() {
var node = document.querySelectorAll("span.username")[0];
currentUser = node.textContent;
var key = currentUser;
console.log("current user is:" + key);
}
// This is just a handler to wait to my basemap gallery loads before trying to save
var basemapMod = document.getElementsByClassName("basemap")[0];
basemapMod.addEventListener('click', ()=>{
setTimeout(
function() {
BaseMapSaver();
}, 2000);
});
function BaseMapSaver() {
savebtnBM.addEventListener('click', ()=>{
checkUser();
// This is where I get the data from my API to save, gathers fine
basemapSaved = app.widget.Manager.Gallery.activeBasemap.Item.id;
// Below I am trying to set it, at first without the object key but would like to use the key
var saveMap = localStorage.setItem('userA', JSON.stringify(userA));
console.log(userA);
});
}
// Home button
var defaultViewHbtn = document.getElementById("home");
defaultViewHbtn.addEventListener('click', ()=>{
checkUser();
// Here I try to parse the value from local storage object
const userAParseValue = JSON.parse(localStorage.getItem('userA'));
// Errors with Uncaught TypeError: Cannot read property 'BaseMap' of undefined
userBaseMap = userAParseValue.userA.BaseMap.basemapSaved;
console.log(userBaseMap);
app.widget.Manager.Gallery.activeBasemap.Item.id = {
portalItem: {
id: userA.BaseMap.basemapSaved // this is where I want to load saved value from local storage object
}
};
});
It Should work check addEventListener function:-
Hbtn.addEventListener('click', ()=>{
checkUser();
const userAParseValue = JSON.parse(localStorage.getItem('userA'));
userBaseMap = userAParseValue.userA.BaseMap.basemapSaved;
console.log(userBaseMap);
myApp.widgets.bigData.Gallery.map = {
Item: {
id: userA.BaseMap.basemapSaved
}
};
});
You can use localStorage and the approach you're trying to make work, but you'll end up with localStorage having a separate object for each user. If that's OK then you use localStorage after load to check if a user is logged in and then load the users' data. Then update the data to the localStorage when the values change. You may check inline comments for details:
HTML if there is a user logged in:
<h3>User <span class="username"><?php echo $user; ?></span> is logged in</h3>
<form method="post">
<input type="hidden" name="action" value="logout"/>
<button type="submit">Logout</button>
</form>
<hr/>
<div>
<h2>User counter: <span id="counter"></span></h2>
<div>
<button id="inc-counter">Increase</button>
<button id="dec-counter">Decrease</button>
</div>
</div>
Javascript to handle localStorage:
// Get user stored on page HTML
const user = document.querySelector("span.username");
// Something to update/alter using user data and/or user input
const counter = document.querySelector("#counter");
const incCounter = document.querySelector("#inc-counter");
const decCounter = document.querySelector("#dec-counter");
if(user) { // If there is a user logged in
// Get the username
const username = user.textContent;
// Get the localStorage the belongs to that user (using username for key)
let storageUser = JSON.parse(localStorage.getItem(username) || 'null');
// Use default user object if the user has no previous settings stored
let currentUser = storageUser || {
BaseMap: {
counter: 0
}
};
// Display the user data
function displayCounter() {
const BaseMap = 'BaseMap' in currentUser ? currentUser.BaseMap : {};
let userCounter = 'counter' in BaseMap ? BaseMap.counter : 0;
counter.textContent = userCounter;
}
// Alter the user data and save it to localStorage user settings object
function alterCounter(addToCounter) {
// Check if BaseMap object exists or default
const BaseMap = 'BaseMap' in currentUser ? currentUser.BaseMap : {};
// Check if data exists or default
let userCounter = 'counter' in BaseMap ? BaseMap.counter : 0;
// Alter user data according to user input
userCounter += addToCounter;
// Change user settings object
currentUser['BaseMap']['counter'] = userCounter;
// Save user settings object
localStorage.setItem(username, JSON.stringify(currentUser));
// Display altered user data
displayCounter();
}
// Initialize by display retrieved/default data
displayCounter();
// Add event listeners to user inputs
incCounter.addEventListener('click', () => alterCounter(1));
decCounter.addEventListener('click', () => alterCounter(-1));
}
You can check an online example that I've made at the link below:
https://zikro.gr/dbg/so/60010743/ (Users userA, userB both with password 1234 can be used for demonstration)
That will work and retrieve/save user data to the localStorage using username for each user. Keep in mind that this method will only save the user settings for a specific browser location. If you want to have user settings when the user logs in from anywhere, then you should go with the traditional workaround which is based on server session, but it's not so flexible when it comes to user settings because you'll have to update each data/setting using server requests each time the user makes a change which it's possible but it requires server + client implementation.
A combination of both server side settings storage + server session + client localStorage would be the best approach to this situation.
here is my answer
<html>
<body>
<span class="border username">121</span>
<div class="border basemap">Base Map</div>
<div class="border saveBtn">Save</div>
<div id="home" class="border">Home</div>
<style>
.border{
border: solid gray 1px;
border-radius: 2px;
text-align: center;
background: #eee;
margin: 5px;
width: 100px;
}
</style>
<script type="text/javascript">
// Here I am trying to initialize the variables
var key = 1;
var currentUser;
var basemapSaved;
var userA = {
BaseMap: {
id: 1234
}
};
var app = {
widget: {
Manager: {
Gallery: {
activeBasemap: {
Item: {
id: {
portalItem: {
id: 1234 // this is where I want to load saved value from local storage object
}
}
}
}
}
}
}
};
// This function I am getting the logged in username, I want to set this as the object key in userA i.e. userA[key]
function checkUser() {
var node = document.querySelectorAll("span.username")[0];
currentUser = node.textContent;
var key = currentUser;
console.log("current user is:" + key);
}
// This is just a handler to wait to my basemap gallery loads before trying to save
var basemapMod = document.getElementsByClassName("basemap")[0];
basemapMod.addEventListener('click', ()=>{
console.log("basemapMod click");
setTimeout(
function() {
BaseMapSaver();
}, 2000);
});
function BaseMapSaver() {
var savebtnBM = document.getElementsByClassName("saveBtn")[0];
savebtnBM.addEventListener('click', ()=>{
console.log("savebtnBM click");
checkUser();
// This is where I get the data from my API to save, gathers fine
basemapSaved = app.widget.Manager.Gallery.activeBasemap.Item.id.portalItem.id;
/** saving users, instead of userA */
const userAParseValue = JSON.parse(localStorage.getItem('users'));
userA.BaseMap.basemapSaved = basemapSaved;
const finalUsers = {...userAParseValue, userA}
// Below I am trying to set it, at first without the object key but would like to use the key
var saveMap = localStorage.setItem('users', JSON.stringify(finalUsers));
console.log(userA);
});
}
// Home button
var defaultViewHbtn = document.getElementById("home");
defaultViewHbtn.addEventListener('click', ()=>{
console.log("defaultViewHbtn click");
checkUser();
// Here I try to parse the value from local storage object
const userAParseValue = JSON.parse(localStorage.getItem('users'));
// Errors with Uncaught TypeError: Cannot read property 'BaseMap' of undefined
userBaseMap = userAParseValue.userA.BaseMap.basemapSaved;
console.log(userBaseMap);
app.widget.Manager.Gallery.activeBasemap.Item.id = {
portalItem: {
id: userA.BaseMap.basemapSaved // this is where I want to load saved value from local storage object
}
};
});
</script>
</body>
</html>
I changed a few structures which were not coherent. Saving and loading them was causing discrepancies. I also suggest storing all users in a single object and accessing the data from userMap because multiple users can use same browser.
Based on the two requirements that you have defined in your original question, this should do what you ask.
The goal is the user can save up to 7 field vals in obj userA, logout, log back in and the saved vals are there, retrievable. Specific to each user.
I am trying to create an object i.e. userA and update it, as the user saves each field value (i.e. BaseMap: basemapSaved), save the updated state in local storage, then retrieve saved state using local storage. So, when the user logs out, then logs back in, their saved data is still there specific to their username.
// retrieve user from localstorage. defaults to {}
// This looks to retrieve the user from local storage by username.
// Returns a `userObj` an object with two properties.
// `username` - the name of the user
// `user` - the stored object that was retrieved from local storage.
// defaults to {} if nothing in user storage
// Not a good strategy btw, a lot of us share the same names :)
function getUser(username) {
let user = localStorage.getItem(username) || {};
try {
user = JSON.parse(user);
} catch (e) {
user = {};
}
return { username, user }
}
// Store user object in localstorage
// Store a user in local storage, keyed by their username
function setUser(username, user) {
localStorage.setItem(username, JSON.stringify(user));
}
// set a key/ value on user object in localstorage
// Don't allow anymore than 7 properties to be stored on the user object
function setUserProperty(userObj, key, value) {
let { username, user } = userObj;
if (Object.keys(user).length > 7) {
throw new Error('user properties exceeds 7')
}
user[key] = value;
setUser(username, user);
}
// modified to return a user from local storage or {}
function checkUser() {
var node = document.querySelectorAll("span.username")[0];
const currentUser = node.textContent;
return getUser(currentUser);
}
// This is just a handler to wait to my basemap gallery loads before trying to save
var basemapMod = document.getElementsByClassName("basemap")[0];
basemapMod.addEventListener('click', () => {
setTimeout(
function() {
BaseMapSaver();
}, 2000);
});
// Fyi Capitals indicate constructors - not functions!
function BaseMapSaver() {
savebtnBM.addEventListener('click', () => {
const userObj = checkUser(); // get the user from localstorage
const basemapSaved = app.widget.Manager.Gallery.activeBasemap.Item.id;
setUserProperty(userObj, 'basemap', basemapSaved) // store the basemap on the user object in local storage with the key 'basemap'
console.log(JSON.stringify(userObj));
});
}
var defaultViewHbtn = document.getElementById("home");
defaultViewHbtn.addEventListener('click', () => {
// get user from localstorage
const { user } = checkUser();
const userBaseMap = user.basemap
// if we have a store basemap
if (userBaseMap) {
app.widget.Manager.Gallery.activeBasemap.Item.id = {
portalItem: {
id: userBaseMap // load it
}
};
}
});
There are many ways to handle this depending upon your use case. You have specifically mentioned LocalStorage hence everyone is suggesting the same but cookies will fit your bill as well as long as you handle the expiry time properly for them.
Local Storage
Make an Object of fields you will like to store for that user
let obj = {'keyname' : value, 'keyname' : value};
//store it - mapping it with user
localStorage.setItem('userID', JSON.stringify(obj));
//retrieve and use on login success
let ret_obj= localStorage.getItem('userID');
Cookies
You can set an arbitrary expiration time and then you again have choice of choosing just one variable or store it as a JSON itself.
document.cookie = "userName=Robert; expires=Fri, 31 Dec 9999 23:59:59 GMT";
*Cookies will hold limited amount of data, as in not huge data (Which I don't think is the use case here because I checked your jsfiddle example, you are basically trying to store some data)
If you want to store JSON data in cookies check this out Click Here
*Why am I suggesting cookies? Many enterprises already do something similar for example even post logging out when you visit a website
they will display your name and ask you to sign-in, it is just a
personalisation addition.
I want to write an Angular module that saves data locally (with IndexedDB) and sync that data with server data via a RESTful service.
I have build a rudimental base for that already that can get data to the server and put it into the IndexedDB.
Because the web application has to run in offline mode too, I have chosen to use two keys for the object stores. A local ID which is autoIncremented and the ID of the entry on the server.
The server isn't aware of the local ID and the local data entry might not know the server ID if it can't be transmitted to the server
When I define an unique index for the server ID, the local entries won't get updated if the server ID already exists and the update process stops.
Is there a way to do this directly with the IDB API?
I have found a similar problem, but with the an simple cursor and cursor.update solution, there is no possibility to insert new data from the server to the local DB:
Indexeddb - Update record by index key
I have found a way to do this. I will handle the local data (with an local id) separately from the server data (no local id, because server doesn't know it).
Then i will update the local data with IDBObjectStore.put and check if the data that came from the server (server id is set, but no local id) is already saved locally with separate IDBIndex.openCursor calls. If local data was found with the given serverId, a new entry will be added with put IDBObjectStore.put, and if found the cursor on that entry will be updated with the server data (old localId will be carried over).
// var _db => connected database
// filter the data that came from the server (no local id defined but server id is)
var serverData = [];
for (var i = 0; i < data.length; i++) {
if (data[i]._localId === undefined && data[i].serverId !== undefined && data[i].serverId !== null) {
// remove the server data object and add it to the server data array
serverData = serverData.concat(data.splice(i, 1));
i--;
}
}
var transaction = _db.transaction(_storageName, 'readwrite');
transaction.oncomplete = function() {
// do something on completion
};
transaction.onerror = function(e) {
// do something when an error occurs
};
var objectStore = transaction.objectStore(_storageName);
// Add local data to the database (no server id)
// local id can be existing or not (new entry)
for (var i = 0; i < data.length; i++) {
objectStore.put(data[i]).onsuccess = function(e) {
// do something when successfully added
};
}
// Add data from the server to the database
var index = objectStore.index('serverId'); // server id index for searching
// go through all data from the server
for (var i = 0; i < serverData.length; i++) {
(function(){
var serverItem = serverData[i];
// search for an existing entry in the local database
var checkRequest = index.openCursor(IDBKeyRange.only(serverItem.serverId));
checkRequest.onsuccess = function (e) {
var cursor = e.target.result;
// If item was not found in local indexedDB storage...
if (cursor === null) {
// add new item to storage
this.source.objectStore.put(serverItem).onsuccess = function(e) {
// do something when successfully added
};
// Item was found locally
} else {
var dbItem = cursor.value;
// set local id of the added item to the one from the old local entry
serverItem.localId = dbItem.localId;
// update found local entry with the one from the server
cursor.update(serverItem).onsuccess = function(e) {
// do something on success
};
}
};
})();
}
There could be a more elegant solution, but this one is the first one that I came up with, that worked. I would be thankful for any improvement or better solutions.
For how long is data stored in localStorage (as part of DOM Storage in HTML5) available? Can I set an expiration time for the data which I put into local storage?
I would suggest to store timestamp in the object you store in the localStorage
var object = {value: "value", timestamp: new Date().getTime()}
localStorage.setItem("key", JSON.stringify(object));
You can parse the object, get the timestamp and compare with the current Date, and if necessary, update the value of the object.
var object = JSON.parse(localStorage.getItem("key")),
dateString = object.timestamp,
now = new Date().getTime().toString();
compareTime(dateString, now); //to implement
Alternatively, you could use a light-weight wrapper like localstorage-slim.js which handles this for you.
It's not possible to specify expiration. It's completely up to the user.
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
Of course, it's possible that something your application stores on the client may not be there later. The user can explicitly get rid of local storage, or the browser may run into space considerations. It's good to program defensively. Generally however things remain "forever" based on some practical definition of that word.
edit — obviously, your own application can actively remove stuff if it decides it's too old. That is, you can explicitly include some sort of timestamp in what you've got saved, and then use that later to decide whether or not information should be flushed.
You can use lscache. It handles this for you automatically, including instances where the storage size exceeds the limit. If that happens, it begins pruning items that are the closest to their specified expiration.
From the readme:
lscache.set
Stores the value in localStorage. Expires after specified number of minutes.
Arguments
key (string)
value (Object|string)
time (number: optional)
This is the only real difference between the regular storage methods. Get, remove, etc work the same.
If you don't need that much functionality, you can simply store a time stamp with the value (via JSON) and check it for expiry.
Noteworthy, there's a good reason why local storage is left up to the user. But, things like lscache do come in handy when you need to store extremely temporary data.
Brynner Ferreira, has brought a good point: storing a sibling key where expiration info resides. This way, if you have a large amount of keys, or if your values are large Json objects, you don't need to parse them to access the timestamp.
here follows an improved version:
/* removeStorage: removes a key from localStorage and its sibling expiracy key
params:
key <string> : localStorage key to remove
returns:
<boolean> : telling if operation succeeded
*/
function removeStorage(name) {
try {
localStorage.removeItem(name);
localStorage.removeItem(name + '_expiresIn');
} catch(e) {
console.log('removeStorage: Error removing key ['+ key + '] from localStorage: ' + JSON.stringify(e) );
return false;
}
return true;
}
/* getStorage: retrieves a key from localStorage previously set with setStorage().
params:
key <string> : localStorage key
returns:
<string> : value of localStorage key
null : in case of expired key or failure
*/
function getStorage(key) {
var now = Date.now(); //epoch time, lets deal only with integer
// set expiration for storage
var expiresIn = localStorage.getItem(key+'_expiresIn');
if (expiresIn===undefined || expiresIn===null) { expiresIn = 0; }
if (expiresIn < now) {// Expired
removeStorage(key);
return null;
} else {
try {
var value = localStorage.getItem(key);
return value;
} catch(e) {
console.log('getStorage: Error reading key ['+ key + '] from localStorage: ' + JSON.stringify(e) );
return null;
}
}
}
/* setStorage: writes a key into localStorage setting a expire time
params:
key <string> : localStorage key
value <string> : localStorage value
expires <number> : number of seconds from now to expire the key
returns:
<boolean> : telling if operation succeeded
*/
function setStorage(key, value, expires) {
if (expires===undefined || expires===null) {
expires = (24*60*60); // default: seconds for 1 day
} else {
expires = Math.abs(expires); //make sure it's positive
}
var now = Date.now(); //millisecs since epoch time, lets deal only with integer
var schedule = now + expires*1000;
try {
localStorage.setItem(key, value);
localStorage.setItem(key + '_expiresIn', schedule);
} catch(e) {
console.log('setStorage: Error setting key ['+ key + '] in localStorage: ' + JSON.stringify(e) );
return false;
}
return true;
}
Here highly recommended to use sessionStorage
it is same as localStorage but destroy when session destroyed / browser close
also localStorage can share between tabs while sessionStorage can use in current tab only, but value does not change on refresh page or change the page
sessionStorage is also useful to reduce network traffic against cookie
for set value use
sessionStorage.setItem("key","my value");
for get value use
var value = sessionStorage.getItem("key");
click here for view api
all ways for set are
sessionStorage.key = "my val";
sessionStorage["key"] = "my val";
sessionStorage.setItem("key","my value");
all ways for get are
var value = sessionStorage.key;
var value = sessionStorage["key"];
var value = sessionStorage.getItem("key");
While local storage does not supply an expiration mechanism, cookies do. Simply pairing a local storage key with a cookie provides an easy way to ensure that local storage can be updated with the same expiration parameters as a cookie.
Example in jQuery:
if (!$.cookie('your_key') || !localStorage.getItem('your_key')) {
//get your_data from server, then...
localStorage.setItem('your_key', 'your_data' );
$.cookie('your_key', 1);
} else {
var your_data = localStorage.getItem('your_key');
}
// do stuff with your_data
This example sets a cookie with the default parameter to expire when the browser is closed. Thus, when the browser is closed and re-opened, the local data store for your_data gets refreshed by a server-side call.
Note that this is not exactly the same as removing the local data store, it is instead updating the local data store whenever the cookie expires. However, if your main goal is to be able to store more than 4K client-side (the limitation for cookie size), this pairing of cookie and local storage will help you to accomplish a larger storage size using the same expiration parameters as a cookie.
The lifecycle is controlled by the application/user.
From the standard:
User agents should expire data from the local storage areas only for security reasons or when requested to do so by the user. User agents should always avoid deleting data while a script that could access that data is running.
From the W3C draft:
User agents should expire data from the local storage areas only for security reasons or when requested to do so by the user. User agents should always avoid deleting data while a script that could access that data is running.
You'll want to do your updates on your schedule using setItem(key, value); that will either add or update the given key with the new data.
// Functions
function removeHtmlStorage(name) {
localStorage.removeItem(name);
localStorage.removeItem(name+'_time');
}
function setHtmlStorage(name, value, expires) {
if (expires==undefined || expires=='null') { var expires = 3600; } // default: 1h
var date = new Date();
var schedule = Math.round((date.setSeconds(date.getSeconds()+expires))/1000);
localStorage.setItem(name, value);
localStorage.setItem(name+'_time', schedule);
}
function statusHtmlStorage(name) {
var date = new Date();
var current = Math.round(+date/1000);
// Get Schedule
var stored_time = localStorage.getItem(name+'_time');
if (stored_time==undefined || stored_time=='null') { var stored_time = 0; }
// Expired
if (stored_time < current) {
// Remove
removeHtmlStorage(name);
return 0;
} else {
return 1;
}
}
// Status
var cache_status = statusHtmlStorage('cache_name');
// Has Data
if (cache_status == 1) {
// Get Cache
var data = localStorage.getItem('cache_name');
alert(data);
// Expired or Empty Cache
} else {
// Get Data
var data = 'Pay in cash :)';
alert(data);
// Set Cache (30 seconds)
if (cache) { setHtmlStorage('cache_name', data, 30); }
}
If you’re familiar with the browsers locaStorage object, you know that there’s no provision for providing an expiry time. However, we can use Javascript to add a TTL (Time to live) to invalidate items in locaStorage after a certain period of time elapses.
function setLocalStorageItem(key, value, ttl) {
// `item` is an object which contains the original value
// as well as the time when it's supposed to expire
let item = {
value: value,
expiry: ttl ? Date.now() + ttl : null
};
localStorage.setItem(key, JSON.stringify(item));
}
function getLocalStorageItem(key) {
let item = localStorage.getItem(key);
// if the item doesn't exist, return null
if (!item) return null;
item = JSON.parse(item);
// compare the expiry time of the item with the current time
if (item.expiry && Date.now() > item.expiry) {
// If the item is expired, delete the item from storage and return null
localStorage.removeItem(key);
return null;
}
return item.value;
}
You can try this one.
var hours = 24; // Reset when storage is more than 24hours
var now = Date.now();
var setupTime = localStorage.getItem('setupTime');
if (setupTime == null) {
localStorage.setItem('setupTime', now)
} else if (now - setupTime > hours*60*60*1000) {
localStorage.clear()
localStorage.setItem('setupTime', now);
}
If anyone still looking for a quick solution and don't want dependencies like jquery etc I wrote a mini lib that add expiration to local / session / custom storage, you can find it with source here:
https://github.com/RonenNess/ExpiredStorage
If someone using jStorage Plugin of jQuery the it can be add expiry with setTTL function if jStorage plugin
$.jStorage.set('myLocalVar', "some value");
$.jStorage.setTTL("myLocalVar", 24*60*60*1000); // 24 Hr.
Javascript localStorage do not have any options to set expire time
Then i use these functions to check supports, Set and Get
function ls_support(){
return "localStorage" in window&&window["localStorage"]!==null;
}
function lsset(key,val,exp){
if(ls_support()){
if(!exp) exp=600;// = 10 minutes Default
localStorage[key]=
JSON.stringify({
"val":val,
"exp":~~((new Date()).getTime()/1000)+exp
});
}
}
function lsget(key){
if(ls_support()){
str=localStorage[key];
if("undefined"!=typeof(str)&&str.length){
try{// is json or not
json=JSON.parse(str);
}catch(e){// if variable not set via lsset func
//json.exp=false;// will return null
return str;// will return original variable
}
if(json.exp){// variable setted via lsset func
if(~~((new Date()).getTime()/1000)>json.exp){// expired
delete localStorage[key];
}else{
return json.val;
}
}
}
}
return null;
}
And it seems works fine :
Workaround using angular and localforage:
angular.module('app').service('cacheService', function() {
return {
set: function(key, value, expireTimeInSeconds) {
return localforage.setItem(key, {
data: value,
timestamp: new Date().getTime(),
expireTimeInMilliseconds: expireTimeInSeconds * 1000
})
},
get: function(key) {
return localforage.getItem(key).then(function(item) {
if(!item || new Date().getTime() > (item.timestamp + item.expireTimeInMilliseconds)) {
return null
} else {
return item.data
}
})
}
}
})
#sebarmeli's approach is the best in my opinion, but if you only want data to persist for the life of a session then sessionStorage is probably a better option:
This is a global object (sessionStorage) that maintains a storage area
that's available for the duration of the page session. A page session
lasts for as long as the browser is open and survives over page
reloads and restores. Opening a page in a new tab or window will cause
a new session to be initiated.
MDN: sessionStorage
For the benefit of searchers:
Like Fernando, I didn't want to add a load of json when the values stored were simple. I just needed to track some UI interaction and keep the data relevant (e.g. how a user used an ecommerce site before checking out).
This will not meet everyones criteria, but will hopefully be a quick copy+paste starter for someone and save adding another lib.
NOTE: This would not be good if you need to retrieve the items individually.
// Addition
if(window.localStorage){
localStorage.setItem('myapp-' + new Date().getTime(), 'my value');
}
// Removal of all expired items
if(window.localStorage){
// two mins - (1000 * 60 * 20) would be 20 mins
var expiryTime = new Date().getTime() - (1000 * 60 * 2);
var deleteRows = [];
for(var i=0; i < localStorage.length; i++){
var key = localStorage.key(i);
var partsArray = key.split('-');
// The last value will be a timestamp
var lastRow = partsArray[partsArray.length - 1];
if(lastRow && parseInt(lastRow) < expiryTime){
deleteRows.push(key);
}
}
// delete old data
for(var j=0; j < deleteRows.length; j++){
localStorage.removeItem(deleteRows[j]);
}
}
function setStorage(name,value){
return localStorage.setItem(name,JSON.stringify({value:value,timestamp:Math.round((new Date()).getTime()/1000)}));
}
function getStorage(name,timeout){
var object = JSON.parse(localStorage.getItem(name));
if(object){
if(Math.round((new Date()).getTime()/1000) < (object.timestamp+timeout)){
return object.value;
}else{
localStorage.removeItem(name);
}
}
return false;
}