How to apply expiry times to keys in HTML5 indexeddb? - html

In indexedDB in html5 api, I can use it to store key-value pairs. But how can I make sure that after adding a certain key-value, after 1 day, that key should automatically be deleted from the db.
I was thinking of wrapping the value in an object with current datetime, and expiry time, and when u get the value, check the time difference, but is this the best way?
Thanks

Yep, what Scott Marcus and dgrogan said. One more hint: if you create an index on the timestamp, you can iterate a cursor over the range of "expired" values and delete them after opening the database.
const open = indexedDB.open("demo");
open.onupgradeneeded = function () {
const db = open.result;
const store = db.createObjectStore("store");
const index = store.createIndex("timestamp", "timestamp");
// Populate with some dummy data, with about half from the past:
for (let id = 0; id < 20; ++id) {
store.put(
{
value: Math.random(),
timestamp: new Date(Date.now() + (Math.random() - 0.5) * 10000),
},
id
);
}
};
open.onsuccess = function () {
const db = open.result;
const tx = db.transaction("store", "readwrite");
// Anything in the past:
const range = IDBKeyRange.upperBound(new Date());
tx
.objectStore("store")
.index("timestamp")
.openCursor(range).onsuccess = function (e) {
const cursor = e.target.result;
if (!cursor) return;
console.log("deleting: " + cursor.key);
cursor.delete();
cursor.continue();
};
// This transaction will run after the first commits since
// it has overlapping scope:
const tx2 = db.transaction("store");
tx2.objectStore("store").count().onsuccess = function (e) {
console.log("records remaining: " + e.target.result);
};
};

Support for automatically expiring IndexedDB and other storage data is being considered. See https://github.com/whatwg/storage/issues/11. It would be helpful if you could describe your use case there.
In the meantime you'll have to do something like you outlined or what Scott Marcus suggests.

IndexedDB can only be modified by code. It has no automatic capabilities. Why not just store the data with a timestamp value and modify your code to remove it when the timestamp is out of date?

I wrote a npm module sometime back which expires indexedDB keys after x mins. Also it has very simple api (similar to memcache/localstorage) and is suited for use as a key-value store.
You can check it on npm here .

Related

Select specific data in JSON output

I've created a function that does a http request and then saves some data from the JSON output.
$scope.addMovie = function() {
'http://api.themoviedb.org/3/movie/206647?api_key=a8f7039633f2065942cd8a28d7cadad4&append_to_response=releases'
// Search for release dates using the ID.
var base = 'http://api.themoviedb.org/3/movie/';
var movieID = $(event.currentTarget).parent().find('.movieID').text()
var apiKey = 'a8f7039633f2065942cd8a28d7cadad4&query='
var append_to_response = '&append_to_response=releases'
var callback = 'JSON_CALLBACK'; // provided by angular.js
var url = base + movieID + '?api_key=' + apiKey + append_to_response + '&callback=' + callback;
$http.jsonp(url,{ cache: true}).
success(function(data, status, headers, config) {
if (status == 200) {
// $scope.movieListID = data.results;
$scope.movieListID = data;
console.log($scope.movieListID);
createMovie.create({
title: $scope.movieListID.original_title,
release_date: $scope.movieListID.release_date,
image: $scope.movieListID.poster_path
}).then(init);
} else {
console.error('Error happened while getting the movie list.')
}
})
};
This function saves the title, release date en posterpath and that works fine. The problem is that it only saves one release_date while the JSON output has a lot more, but I don't know how to acces that.
This is a example of the JSON output I request
It has a release_date, which I save now, but it also has more information,
releases":{
"countries":[
{"certification":"","iso_3166_1":"GB","primary":true,"release_date":"2015-10-26"},
{"certification":"","iso_3166_1":"US","primary":false,"release_date":"2015-11-06"},
{"certification":"","iso_3166_1":"NL","primary":false,"release_date":"2015-11-05"},
{"certification":"","iso_3166_1":"BR","primary":false,"release_date":"2015-11-05"},
{"certification":"","iso_3166_1":"SE","primary":false,"release_date":"2015-11-04"},
{"certification":"","iso_3166_1":"IE","primary":false,"release_date":"2015-10-26"},
How would I go about saving the release date for the NL release?
You just need to iterate through the countries array, and check if the country code matches the one you wish to retrieve. For your example with 'NL':
var releaseNL;
for (var i = 0; i < $scope.movieList.releases.countries.length; i++) {
var release = $scope.movieList.releases.countries[i];
if (release['iso_3166_1'] == 'NL') {
releaseNL = release;
}
}
This is just one of many ways to do this (e.g. you could use angular.forEach, wrap it inside a function, etc.), but this should give you an idea.
Remark: I noticed you have been asking a lot of very basic questions today, which you could easily answer yourself with a bit more research. E.g. this question is not even AngularJS related, but just a simple JavaScript task. So maybe try to show a bit more initiative next time! ;)

Read records from firebase based on a previously saved value

I took an angularjs + firebase example and modified it for an app where I can register some kids for a small cross-country race.
I'm able to register kids (participants), races, locations, clubs etc. using a basic structure:
FIREBASE_URL/races
FIREBASE_URL/clubs
and so forth. When the active race is selected, I save the raceId and race json-object and can add participants to the active race.
Example:
FIREBASE_URL/active_race/-JI6H9VQewd444na_CQY
FIREBASE_URL/active_race/json-object
What I'd like to do is to get all the participants, if any, based on raceId:
FIREBASE_URL/races/-JI6H9VQewd444na_CQY/participants
I tried the following
'use strict';
app.factory('Race', function ($firebase, FIREBASE_URL, User) {
var ref = new Firebase(FIREBASE_URL + 'races');
var races = $firebase(ref);
var Race = {
all: races,
getParticipantsInRace: function () {
var fb = new Firebase(FIREBASE_URL);
fb.child('active_race/raceId').once('value', function (activeSnap) {
races.$child('/' + activeSnap.val() + '/participants');
});
}
};
return Race;
But I believe I'm doing it wrong. I tried to prepend return before races.$child and fb.child but it did not solve my problem.
I tried to hardcode the following json-array and this is shown on the webpage:
return [{name: 'Claus', born: '1967'}, {name: 'John', born: '1968'}];
How do I get all the participants into $scope.participantsInRace?
I believe I have a solution, but I'm not sure if it's wise to do it this way. But it may be that simple. Prepending $rootScope.participantsInRace = to put it into rootScope:
$rootScope.participantsInRace = races.$child('/' + activeSnap.val() + '/participants');
The code is already synchronizing all data in all races when it declares $firebase(URL+'races');. Additionally, you never assigned your races.$child(...) to anything, so it's not possible to reference that data later.
app.factory('Race', function ($firebase, FIREBASE_URL, User) {
var ref = new Firebase(FIREBASE_URL + 'races');
var races = $firebase(ref);
var Race = {
all: races,
getParticipantsInRace: function (raceId) {
return races[raceId]? races[raceId].participants || {};
}
};
return Race;
});
Keep in mind that the race data won't be available until races.$on('loaded') is invoked (when the data returns from the server).
Thank you for the input. I know a bit more about angularjs and javascript now so I did some refactoring and cleanup. Hardcoding raceId works:
getParticipantsInRace: function () {
return races.$child('-JIecmbdDa4kUT2L51iS').$child('participants');
}
When I wrap it in a call to Firebase I can't seem to return the desired data, probably due to my somewhat limited knowledge of javascript on how to return data. Example:
getParticipantsInRace: function () {
ref.child('activeRace').child('raceId').once('value', function (activeSnap) {
return races.$child(activeSnap.val()).$child('participants');
});
}
My idea is to get the raceId and then return all participants. I tried to prepend return to ref.child() but still no data was returned. So not really an answer.
Regards
Claus
This works. I changed $rootScope.participantsInRace to $scope.participantsInRace and the following:
getParticipantsInRace: function () {
if (User.signedIn()) {
var t = [];
var user = User.getCurrent();
var fb = new Firebase(FIREBASE_URL + 'users');
fb.child(user.username).child('activeRace/raceId').once('value', function (userSnap) {
t = races.$child(userSnap.val()).$child('participants');
});
return t;
}
},

IndexedDB can make database unreachable (getting blocked), how unblock?

UPDATE
I discovered the issue is that it's blocked. Despite the database always being created and upgraded by the same extension, it does not get closed. So now I'm getting the "onblocked" function called.
How do I "unblock" currently blocked databases? And how do I prevent this in the future? This is an app, so no tabs are using it. And since I can't open those databases to even delete them (this also gets blocked), how do I close them?
(For anyone wondering, to avoid this issue from the start, you HAVE to do the folllowing:)
mydb.onversionchange = function(event) {
mydb.close();
};
Original Post
IndexedDB dies and becomes unopenable if I (accidentally) try to open and upgrade with the wrong version. As far as I can tell, there's no way to ask indexedDB for the latest version of a DB. So if I try to run the following code twice, it destroys the database and it becomes unopenable:
And it never throws an error or calls onerror. It just sits silently
var db = null;
//Note, no version passed in, so the second time I do this, it seems to cause an error
var req = indexedDB.open( "test" );
req.onsuccess = function(event) { console.log( "suc: " + event.target.result.version ); db = event.target.result; };
req.onerror = function(event) { console.log( "err: " + event ); };
req.onupgradeneeded = function(event) { console.log( "upg: " + event.target.result.version ); };
//We're doing in interval since waiting for callback
var intv = setInterval(
function()
{
if ( db === null ) return;
clearInterval( intv );
var req2 = indexedDB.open( "test", db.version + 1 );
req2.onsuccess = function(event) { console.log( "suc: " + event.target.result.version ); };
req2.onerror = function(event) { console.log( "err: " + event ); };
req2.onupgradeneeded = function(event) { console.log( "upg: " + event.target.result.version ); };
},
50
);
All of that code is in my chrome.runtime.onInstalled.addListener. So when I update my app, it calls it again. If I call indexedDB.open( "test" ) without passing in the new version and then again run the setInterval function, it causes everything to become unusable and I'm never able to open "test" again. This would be solved if I could query indexedDB for the version of a database prior to attempting to open it. Does that exist?
Maybe this helps?
function getVersion(callback) {
var r = indexedDB.open('asdf');
r.onblocked = r.onerror = console.error;
r.onsuccess = function(event) {
event.target.result.close();
callback(event.target.result.version);
};
}
getVersion(function(version) {
console.log('The version is: %s', version);
});
Ok, based on the convo, this little util function might set you on the path:
var DATABASE_NAME_CONSTANT = 'whatever';
// Basic indexedDB connection helper
// #param callback the action to perform with the open connection
// #param version the version of the database to open or upgrade to
// #param upgradeNeeded the callback if the db should be upgraded
function connect(callback, version, upgradeNeeded) {
var r = indexedDB.open(DATABASE_NAME_CONSTANT, version);
if(upgradeNeeded) r.onupgradeneeded = updateNeeded;
r.onblocked = r.onerror = console.error;
r.onsuccess = function(event) {
console.log('Connected to %s version %s',
DATABASE_NAME_CONSTANT, version);
callback(event.target.result);
};
}
// Now let us say you needed to connect
// and need to have the version be upgraded
// and need to send in custom upgrades based on some ajax call
function fetch() {
var xhr = new XMLHttpRequest();
// ... setup the request and what not
xhr.onload = function(event) {
// if response is 200 etc
// store the json in some variable
var responseJSON = ...;
console.log('Fetched the json file successfully');
// Let's suppose you send in version and updgradeNeeded
// as properties of your fetched JSON object
var targetVersion = responseJSON.idb.targetVersion;
var upgradeNeeded = responseJSON.idb.upgradeNeeded;
// Now connect and do whatever
connect(function(db) {
// Do stuff with the locally scoped db variable
// For example, grab a prop from the fetched object
db.objectStore('asdf').put(responseJSON.recordToInsert);
// If you feel the need, but should not, close the db
db.close();
console.log('Finished doing idb stuff');
}, targetVersion, upgradeNeeded);
}
}
I think it is best to provide the version number always. If you don't how are you going to manage upgrades on the db structure? If you don't its a good chance you will get in a situation where same db versions on a client will have an other database structure, and I don't think that is the thing you want. So I would suggest to keep the version number in a variable.
Also when working with indexeddb you will have to provide an upgrade plan from al previous versions to the current. Meaning version 4 has a certain structure, but you will have to be able to get that same structure from scratch as from version 1,2 and 3

Does HTML5 localStorage have an expiry date? [duplicate]

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;
}

Lifespan of LocalStorage data [duplicate]

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;
}