We can animate and interact, stream, play, and render, but we always come back to the data. Data is the foundation on which we build the majority of our JavaScript applications. In the first part of the book we worked with the JavaScript languages standards for data types, in [Link to Come] we fetched data from a remote source, and in [Link to Come] and [Link to Come] we’ll work with data on the server, manipulating data using APIs and data sources. Data and JavaScript, friends forever.
In this chapter, we’re going to look at ways we can persist data using JavaScript in the browser using cookies, sessionStorage, localStorage, and IndexedDB.
You need to read or set the value of a browser cookie.
Use document.cookie
to set and retrieve cookie values:
document
.
cookie
=
'author=Adam'
;
console
.
log
(
document
.
cookie
);
To encode strings, use encodeURIComponent
, which will remove any commas, semicolons, or whitespace:
const
book
=
'JavaScript Cookbook'
document
.
cookie
=
`title=
${
book
}
`
;
console
.
log
(
document
.
cookie
);
// logs title=JavaScript%20Cookbook%3A%20Programming%20the%20Web
Options can be added to the end of the cookie value and should be with a semicolon:
document
.
cookie
=
'user=Abigail; max-age=86400; path=/'
;
To delete a cookie, set a expiration date for the cookie that has already occurred:
function
eraseCookie
(
key
)
{
const
cookie
=
`
${
key
}
=;expires=Thu, 01 Jan 1970 00:00:00 UTC`
;
document
.
cookie
=
cookie
;
}
Cookies are small bits of data that are stored in the browser. They are often set from the server application and sent to the server with nearly every request. In a browser they are accessed via the document.cookie
object.
Cookies accept the following options, each separated with a semicolon:
domain
: The domain at which the cookie is accessible. If not set, this defaults to the current host location. Specifying a domain, allows the cookie to be accessed at subdomains.
expires
: Sets a time at which the cookie expires. Accepts a date in GMTString format.
max-age
: Sets the length of time that the cookie is valid. Accepts a value in seconds.
path
: The path at which the cookie is accessible (such as /
or /app
). If not specified, the cookie defaults to the current path.
secure
: If set to true
, the cookie will only be transmitted over https.
samesite
: Defaults to strict
. If set to strict
, the cookie will not be sent in cross-site browsing. Alternatively lax
will send cookies on top level GET requests.
In the following example, the user can enter a value which is stored as a cookie. They can then retrieve a specified key’s value and delete the value.
In an HTML file:
<!
DOCTYPE
html
>
<
html
lang
=
"en"
>
<
head
>
<
meta
charset
=
"UTF-8"
/>
<
meta
name
=
"viewport"
content
=
"width=device-width, initial-scale=1.0"
/>
<
meta
http
-
equiv
=
"X-UA-Compatible"
content
=
"ie=edge"
/>
<
style
>
div
{
margin
:
10
px
;
}
.
data
{
width
:
200
px
;
background
-
color
:
yellow
;
padding
:
5
px
;
}
<
/style>
<
title
>
Store
,
retrieve
,
and
delete
a
cookie
<
/title>
<
/head>
<
body
>
<
h1
>
Store
,
retrieve
,
and
delete
a
cookie
<
/h1>
<
form
>
<
div
>
<
label
for
=
"key"
>
Enter
key
:<
/label>
<
input
type
=
"text"
id
=
"key"
/>
<
/div>
<
div
>
<
label
for
=
"value"
>
Enter
value
:<
/label>
<
input
type
=
"text"
id
=
"value"
/>
<
/div>
<
/form>
<
button
id
=
"set"
>
Set
data
<
/button>
<
button
id
=
"get"
>
Get
data
<
/button>
<
button
id
=
"erase"
>
Erase
data
<
/button>
<
p
>
Cookie
value
:<
/p>
<
div
id
=
"cookiestr"
class
=
"data"
><
/div>
<
script
src
=
"cookie.js"
><
/script>
<
/body>
<
/html>
And the associated cookie.js
file:
// set the cookie
function
setData
()
{
const
formKey
=
document
.
getElementById
(
'key'
).
value
;
const
formValue
=
document
.
getElementById
(
'value'
).
value
;
const
cookieVal
=
`
${
formKey
}
=
${
encodeURIComponent
(
formValue
)
}
`
;
document
.
cookie
=
cookieVal
;
}
// retrieve the cookie value for a specified key
function
getData
()
{
const
key
=
document
.
getElementById
(
'key'
).
value
;
const
cookie
=
document
.
getElementById
(
'cookiestr'
);
cookie
.
innerHTML
=
''
;
const
keyValue
=
key
.
replace
(
/([.*+?^=!:${}()|[]/\])/g
,
'\$1'
);
const
regex
=
new
RegExp
(
`(?:^|;)\s?
${
keyValue
}
=(.*?)(?:;|$)`
,
'i'
);
const
match
=
document
.
cookie
.
match
(
regex
);
const
value
=
(
match
&&
decodeURIComponent
(
match
[
1
]))
||
''
;
cookie
.
innerHTML
=
`<p>
${
value
}
</p>`
;
}
// remove the cookie for a specified key
function
removeData
()
{
const
key
=
document
.
getElementById
(
'key'
).
value
;
document
.
getElementById
(
'cookiestr'
).
innerHTML
=
''
;
const
cookie
=
`
${
key
}
=; expires=Thu, 01 Jan 1970 00:00:00 UTC`
;
document
.
cookie
=
cookie
;
}
document
.
getElementById
(
'set'
).
onclick
=
setData
;
document
.
getElementById
(
'get'
).
onclick
=
getData
;
document
.
getElementById
(
'erase'
).
onclick
=
removeData
;
Notice that I am using Regular Expressions to match the cookie values, which have been encoded using encodeURIComponent
.
You want to easily store information for a single session, without running into the size and cross-page contamination problems associated with cookies.
Use the DOM Storage sessionStorage
functionality:
sessionStorage
.
setItem
(
'name'
,
'Franco'
);
sessionStorage
.
city
=
'Pittsburgh'
;
// returns 2
console
.
log
(
sessionStorage
.
length
);
// retrieve individual values
const
name
=
sessionStorage
.
getItem
(
'name'
);
const
city
=
sessionStorage
.
getItem
(
'city'
);
console
.
log
(
`The stored name is
${
name
}
`
);
console
.
log
(
`The stored city is
${
city
}
`
);
// remove an individual item from storage
sessionStorage
.
removeItem
(
'name'
);
// remove all items from storage
sessionStorage
.
clear
();
// returns 0
console
.
log
(
sessionStorage
.
length
);
sessionStorage
allows us to easily store information in the user’s browser for a single session. A session lasts for as long as a single browser tab is open. Once the user closes the browser or tab, the session ends. Opening a new tab of the same page, will start a new browser session.
By comparison, the default behavior of both cookies and localStorage
(discussed in Recipe 3.3) is to persist across sessions. As an example of the differences between these storage methods, Example 3-1 stores information from a form in a cookie, localStorage
and sessionStorage
.
The cookies.js file contains the code necessary to set, retrieve, and erase a given cookie:
// set session cookie
function
setCookie
(
cookie
,
value
)
{
const
cookieVal
=
`
${
cookie
}
=
${
encodeURIComponent
(
value
)
}
;path=/`
;
document
.
cookie
=
cookieVal
;
console
.
log
(
cookieVal
);
}
// each cookie separated by semicolon;
function
getCookie
(
key
)
{
const
keyValue
=
key
.
replace
(
/([.*+?^=!:${}()|[]/\])/g
,
'\$1'
);
const
{
cookie
}
=
document
;
const
regex
=
new
RegExp
(
`(?:^|;)\s?
${
keyValue
}
=(.*?)(?:;|$)`
,
'i'
);
const
match
=
cookie
.
match
(
regex
);
return
match
&&
decodeURIComponent
(
match
[
1
]);
}
// set cookie date to the past to erase
function
eraseCookie
(
key
)
{
const
cookie
=
`
${
key
}
=;path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC`
;
document
.
cookie
=
cookie
;
console
.
log
(
cookie
);
}
And the app.js file contains the rest of the program functionality. No separate JavaScript methods are necessary for working with the sessionStorage
or localStorage
objects — another major difference from cookies:
// set data for both session and cookie
function
setData
()
{
const
key
=
document
.
getElementById
(
'key'
).
value
;
const
{
value
}
=
document
.
getElementById
(
'value'
);
// set sessionStorage
sessionStorage
.
setItem
(
key
,
value
);
// set localStorage
localStorage
.
setItem
(
key
,
value
);
// set cookie
setCookie
(
key
,
value
);
}
function
getData
()
{
try
{
const
key
=
document
.
getElementById
(
'key'
).
value
;
const
session
=
document
.
getElementById
(
'sessionstr'
);
const
local
=
document
.
getElementById
(
'localstr'
);
const
cookie
=
document
.
getElementById
(
'cookiestr'
);
// reset display
session
.
innerHTML
=
''
;
local
.
innerHTML
=
''
;
cookie
.
innerHTML
=
''
;
// sessionStorage
let
value
=
sessionStorage
.
getItem
(
key
)
||
''
;
if
(
value
)
session
.
innerHTML
=
`<p>
${
value
}
</p>`
;
// localStorage
value
=
localStorage
.
getItem
(
key
)
||
''
;
if
(
value
)
local
.
innerHTML
=
`<p>
${
value
}
</p>`
;
// cookie
value
=
getCookie
(
key
)
||
''
;
if
(
value
)
cookie
.
innerHTML
=
`<p>
${
value
}
</p>`
;
}
catch
(
e
)
{
console
.
log
(
e
);
}
}
function
removeData
()
{
const
key
=
document
.
getElementById
(
'key'
).
value
;
// sessionStorage
sessionStorage
.
removeItem
(
key
);
// localStorage
localStorage
.
removeItem
(
key
);
// cookie
eraseCookie
(
key
);
// reset display
getData
();
}
document
.
getElementById
(
'set'
).
onclick
=
setData
;
document
.
getElementById
(
'get'
).
onclick
=
getData
;
document
.
getElementById
(
'erase'
).
onclick
=
removeData
;
You can get and set the data from sessionStorage
accessing it directly, as demonstrated in the solution, but a better approach is to use the getItem()
and setItem()
functions.
Load the example page, add one or more values for the same key, and then click the “Get data” button. The result is displayed in Figure 3-1. No surprises here. The data has been stored in cookies, localStorage
, and sessionStorage
. Now, open the same page in a new tab window, enter the value into the key
for filed, and click the “Get data” button. The activity results in a page like that shown in Figure 3-2.
In the new tab window, the cookie
and localStorage
values persist because the cookie
is session-specific, but the sessionStorage
, which is specific to the tab window, does not.
The screenshots graphically demonstrate one of the key differences between sessionStorage
and cookies, aside from how they’re set and accessed in JavaScript. Hopefully, the images and the example also demonstrate the potential hazards involved when using sessionStorage
, especially in circumstances where cookies have normally been used.
If your website or application users are familiar with the cookie persistence across tabbed windows, sessionStorage
can be an unpleasant surprise. Along with the different behavior, there’s also the fact that browser menu options to delete cookies probably won’t have an impact on sessionStorage
, which could also be an unwelcome surprise for your users. On the other hand, sessionStorage
is incredibly clean to use, and provides a welcome storage option when we want to link storage to a specific tab window only.
One last note on sessionStorage
related to its implementation: both sessionStorage
and localStorage
, covered in the next recipe, are part of the W3C DOM Storage specification. Both are window
object properties, which means they can be accessed globally. Both are implementations of the Storage
object, and changes to the prototype
for Storage
result in changes to both the sessionStorage
and localStorage
objects:
Storage
.
prototype
.
someMethod
=
function
(
param
)
{
...};
...
localStorage
.
someMethod
(
param
);
...
sessionStorage
.
someMethod
(
param
);
Aside from the differences, covered in this recipe and the next, another major difference is that the Storage
objects don’t make a round trip to the server—they’re purely client-side storage techniques.
For more information on the Storage
object, sessionStorage
, localStorage
, or the Storage DOM, consult the specification. See Recipe 3.3 for a different look at how sessionStorage
and localStorage
can be set and retrieved.
You want to shadow form element entries (or any data) in such a way that users can continue where they left off if the browser crashes, the user accidentally closes the browser, or the Internet connection is lost.
You could use cookies if the data is small enough, but that strategy doesn’t work in an offline situation. Another, better approach, especially when you’re persisting larger amounts of data or if you have to support functionality when no Internet connection is present, is to use localStorage
:
const
formValue
=
document
.
getElementById
(
'formelem'
).
value
;
if
(
formValue
)
{
localStorage
.
formelem
=
formValue
;
}
// recover
const
storedValue
=
localStorage
.
formelem
;
if
(
storedValue
)
{
document
.
getElementById
(
'formelem'
).
value
=
storedValue
;
}
Recipe 3.2 covered sessionStorage
, one of the DOM Storage techniques. The localStorage
object interface is the same, with the same approaches to setting the data:
// use item methods
sessionStorage
.
setItem
(
'key'
,
'value'
);
localStorage
.
setItem
(
'key'
,
'value'
);
// use property names directly
sessionStorage
.
keyName
=
'value'
;
localStorage
.
keyName
=
'value'
;
// use the key method
sessionStorage
.
key
(
0
)
=
'value'
;
localStorage
.
key
(
0
)
=
'value'
;
and for getting the data:
// use item methods
value
=
sessionStorage
.
getItem
(
'key'
);
value
=
localStorage
.
getItem
(
'key'
);
// use property names directly
value
=
sessionStorage
.
keyName
;
value
=
localStorage
.
keyName
;
// use the key method
value
=
sessionStorage
.
key
(
0
);
value
=
localStorage
.
key
(
0
);
Again, as with sessionStorage
, though you can access and set data on localStorage
directly, you should use getItem()
and setItem()
, instead.
Both of the storage objects support the length
property, which provides a count of stored item pairs, and the clear
method (no parameters), which clears out all storage. In addition, both are scoped to the HTML5 origin, which means that the data storage is shared across all pages in a domain, but not across protocols (e.g., http
is not the same as https
) or ports.
The difference between the two is how long data is stored. The sessionStorage
object only stores data for the session, but the localStorage
object stores data on the client forever, or until specifically removed.
The sessionStorage
and localStorage
objects also support one event: the storage
event. This is an interesting event, in that it fires on all pages when changes are made to a localStorage
item. It is also an area of low-compatibility among browsers: you can capture the event on the body
or document
elements for Firefox, on the body
for IE, or on the document
for Safari.
Example 3-2 demonstrates a more comprehensive implementation than the use case covered in the solution for this recipe. In the example, all elements of a small form have their onchange
event handler method assigned to a function that captures the change element name and value, and stores the values in the local storage via localStorage
. When the form is submitted, all of the stored form data is cleared.
When the page is loaded, the form elements onchange
event handler is assigned to the function to store the values, and if the value is already stored, it is restored to the form element. To test the application, enter data into a couple of the form fields—but, before clicking the Submit button, refresh the page. Without localStorage
, you’d lose the data. Now, when you reload the page, the form is restored to the state it was in before the page was reloaded.
<!DOCTYPE html>
<html
lang=
"en"
>
<head>
<meta
charset=
"UTF-8"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
/>
<meta
http-equiv=
"X-UA-Compatible"
content=
"ie=edge"
/>
<title>
Creating a localStorage Client-Side Data Storage Item</title>
</head>
<body>
<h1>
Creating a localStorage Client-Side Data Storage Item</h1>
<form
id=
"inputform"
>
<div>
<label
for=
"field1"
>
Enter field1:</label>
<input
type=
"text"
id=
"field1"
/>
</div>
<div>
<label
for=
"field2"
>
Enter field2:</label>
<input
type=
"text"
id=
"field2"
/>
</div>
<div>
<label
for=
"field3"
>
Enter field1:</label>
<input
type=
"text"
id=
"field3"
/>
</div>
<div>
<label
for=
"field4"
>
Enter field1:</label>
<input
type=
"text"
id=
"field4"
/>
</div>
<input
type=
"submit"
value=
"Save"
/>
</form>
<script
src=
"localstorage.js"
></script>
</body>
</html>
In the JavaScript file:
// store the form input elements as a variable
const
elems
=
document
.
querySelectorAll
(
'input'
);
// store field values
function
processField
()
{
localStorage
.
setItem
(
window
.
location
.
href
,
'true'
);
localStorage
.
setItem
(
this
.
id
,
this
.
value
);
}
// clear individual fields
function
clearStored
()
{
elems
.
forEach
(
elem
=>
{
if
(
elem
.
type
===
'text'
)
{
localStorage
.
removeItem
(
elem
.
id
);
}
});
}
// capture submit button to clear storage when clicked
document
.
getElementById
(
'inputform'
).
onsubmit
=
clearStored
;
// on form element change, store the value in localStorage
elems
.
forEach
(
elem
=>
{
if
(
elem
.
type
===
'text'
)
{
const
value
=
localStorage
.
getItem
(
elem
.
id
);
if
(
value
)
elem
.
value
=
value
;
// change event
elem
.
onchange
=
processField
;
}
});
The size allotted for localStorage
varies by browser, but most are in the 5mb-10mb range. You can use a try/catch
block to test to ensure you have not exceeded the limit in the user’s browser:
try
{
localStorage
.
setItem
(
'key'
,
'value'
);
}
catch
(
domException
)
{
if
(
[
'QuotaExceededError'
,
'NS_ERROR_DOM_QUOTA_REACHED'
].
includes
(
domException
.
name
)
)
{
// handle file size exceeded error
}
else
{
// handle any other error
}
}
The localStorage
object can be used for offline work. For the form example, you can store the data in the localStorage
and provide a button to click when connected to the Internet, in order to sync the data from localStorage
to server-side storage.
See Recipe 3.2 for more on the Storage
object, and on sessionStorage
and localStorage
.
You need more sophisticated data storage on the client than what’s provided with other persistent storage methods, such as localStorage
.
In modern browsers, use IndexedDB.
The JavaScript file in Example 3-3 uses IndexedDB to create a database and a data object. Once created, it adds data and then retrieves the first object. A more detailed description of what’s happening is in the discussion.
const
data
=
[
{
name
:
'Joe Brown'
,
age
:
53
,
experience
:
5
},
{
name
:
'Cindy Johnson'
,
age
:
44
,
experience
:
5
},
{
name
:
'Some Reader'
,
age
:
30
,
experience
:
3
}
];
// delete the 'Cookbook' database, so the example can be run more than once
const
delReq
=
indexedDB
.
deleteDatabase
(
'Cookbook'
);
delReq
.
onerror
=
event
=>
{
console
.
log
(
'delete error'
,
event
);
};
// open the 'Cookbook' database with a version of '1'
// or create it if it does not exist
const
request
=
indexedDB
.
open
(
'Cookbook'
,
1
);
// upgradeneeded event is fired when a db is opened
// with a version number higher than the currently stored version (in this case none)
request
.
onupgradeneeded
=
event
=>
{
const
db
=
event
.
target
.
result
;
const
{
transaction
}
=
event
.
target
;
// create a new object store named 'reader' in the database
const
objectStore
=
db
.
createObjectStore
(
'reader'
,
{
keyPath
:
'id'
,
autoIncrement
:
true
});
// create new keys in the object store
objectStore
.
createIndex
(
'experience'
,
'experience'
,
{
unique
:
false
});
objectStore
.
createIndex
(
'name'
,
'name'
,
{
unique
:
true
});
// when all data loaded, log to the console
transaction
.
oncomplete
=
()
=>
{
console
.
log
(
'data finished'
);
};
const
readerObjectStore
=
transaction
.
objectStore
(
'reader'
);
// add each value from the data object to the indexedDB database
data
.
forEach
(
value
=>
{
const
req
=
readerObjectStore
.
add
(
value
);
// console log a message when successfully added
req
.
onsuccess
=
()
=>
{
console
.
log
(
'data added'
);
};
});
// if the request throws an error, log it to the console
request
.
onerror
=
()
=>
{
console
.
log
(
event
.
target
.
errorCode
);
};
// when the data store is successfully created, log to the console
request
.
onsuccess
=
()
=>
{
console
.
log
(
'datastore created'
);
};
// on page click, get a random value from the database and log it to the console
document
.
onclick
=
()
=>
{
const
randomNum
=
Math
.
floor
(
Math
.
random
()
*
3
)
+
1
;
const
dataRequest
=
db
.
transaction
([
'reader'
])
.
objectStore
(
'reader'
)
.
get
(
randomNum
);
dataRequest
.
onsuccess
=
()
=>
{
console
.
log
(
`Name :
${
dataRequest
.
result
.
name
}
`
);
};
};
};
IndexedDB is the specification the W3C and others agreed to when exploring solutions to large data management on the client. Though it is transaction-based, and supports the concept of a cursor, it isn’t a relational database system. It works with JavaScript objects, each of which is indexed by a given key, whatever you decide the key to be.
IndexedDB can be both asynchronous and synchronous. It can be used for larger chunks of data in a traditional server or cloud application, but is also helpful for offline web application use.
Most implementations of IndexedDB don’t restrict data storage size, but if you store more than 50 MB in Firefox, the user will need to provide permission. Chrome creates a pool of temporary storage, and each application can have up to 20% of it. Other agents have similar limitations. All of the main browsers support IndexedDB, except Opera Mini, though the overall support may not be identical.
As the solution demonstrates, the IndexedDB API methods trigger both success and error callback functions, which you can capture using traditional event handling, or as callback, or assign to a function. Mozilla describes the pattern of use with IndexedDB:
Open a database.
Create an object store in upgrading database.
Start a transaction and make a request to do some database operation, like adding or retrieving data.
Wait for the operation to complete by listening to the right kind of DOM event.
Do something with the results (which can be found on the request object).
Starting from the top in the solution, a data object is created with three values to add to the datastore. The database is deleted if it exists, so that the example can be run multiple times. Following, a call to open()
opens the database, if it exists, or creates it, if not. Because the database is deleted before the example is run, it’s re-created. The name and version are both necessary, because the database can be altered only if a new version of the database is opened.
A request object (IDBOpenDBRequest) is returned from the open()
method, and whether the operation succeeds or not is triggered as events on this object. In the code, the onsuccess
event handler for the object is captured to provide a message to the console about the success. You can also assign the database handle to a global variable in this event handler, but the code assigns this in the next event handled, the upgradeneeded
event.
The upgradeneeded
event handler is only invoked when a database doesn’t exist for a given database name and version. The event object also gives us a way to access the IDBDatabase reference, which is assigned to the global variable, db
. The existing transaction can also be accessed via the event object passed as argument to the event handler, and it’s accessed and assigned to a local variable.
The event handler for this event is the only time you’ll be able to create the object store and its associated indexes. In the solution, a datastore named reader
is created, with its key set to an autoincrementing id
. Two other indexes are for the datastore’s name
and experience
fields. The data is also added to the datastore in the event, though it could have been added at a separate time, say when a person submits an HTML form.
Following the upgradeneeded
event handler, the success
and error
handlers are coded, just to provide feedback. Last but not least, the document.onclick
event handler is used to trigger a database access. In the solution, a random data instance is accessed via the database handler, its transaction, the object store, and eventually, for a given key. When the query is successful, the name
field is accessed and the value is printed to the console. Rather than accessing a single value, we can also use a cursor, but I’ll leave that for your own experimentation.
The resulting printouts to the console are, in order:
data
added
data
finished
datastore
created
Name
:
Cindy
Johnson
You’d like to work with IndexedDB in an asynchronous fashion, using JavaScript promises.
Use the IDB library, which offers usability improvements to the IndexedDB API as well as a wrapper for using promises.
The following file imports the IDB
library, creates an IndexedDB data store, and adds data to it:
import
{
openDB
,
deleteDB
}
from
'https://unpkg.com/idb?module'
;
const
data
=
[
{
name
:
'Riley Harrison'
,
age
:
57
,
experience
:
1
},
{
name
:
'Harlow Everly'
,
age
:
29
,
experience
:
5
},
{
name
:
'Abigail McCullough'
,
age
:
38
,
experience
:
10
}
];
(
async
()
=>
{
// for demo purposes, delete existing db on page load
try
{
await
deleteDB
(
'CookbookIDB'
);
}
catch
(
err
)
{
console
.
log
(
'delete error'
,
err
);
}
// open the database and create the data store
const
database
=
await
openDB
(
'CookbookIDB'
,
1
,
{
upgrade
(
db
)
{
// Create a store of objects
const
store
=
db
.
createObjectStore
(
'reader'
,
{
keyPath
:
'id'
,
autoIncrement
:
true
});
// create new keys in the object store
store
.
createIndex
(
'experience'
,
'experience'
,
{
unique
:
false
});
store
.
createIndex
(
'name'
,
'name'
,
{
unique
:
true
});
}
});
// add all of the reader data to the store
data
.
forEach
(
async
value
=>
{
await
database
.
add
(
'reader'
,
value
);
});
})();
In the example, I am loading the idb modules from UNPKG, which allows me to directly access the module from a URL, rather than locally installing it. This works well for demo purposes, but in an application you will want to install the module via npm
and bundle it with your code.
IDB
bills itself as “a tiny library that mostly mirrors the IndexedDB API, but with small improvements that make a big difference to usability.” Using idb
simplifies some of the syntax of IndexedDB along with enabling support for asynchronous code execution with promises
The openDB
method opens a database and returns a promise:
const
db
=
await
openDB
(
name
,
version
,
{
// ...
});
In the following example, a user can add data to the database and retrieve all of the data to be displayed on the page.
In an HTML file:
<!DOCTYPE html>
<html
lang=
"en"
>
<head>
<meta
charset=
"UTF-8"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
/>
<meta
http-equiv=
"X-UA-Compatible"
content=
"ie=edge"
/>
<title>
IDB Discussion Example</title>
<style>
div
{
margin
:
10px
;
}
.data
{
width
:
200px
;
background-color
:
yellow
;
padding
:
5px
;
}
</style>
</head>
<body>
<h1>
IDB Discussion Example</h1>
<form>
<div>
<label
for=
"name"
>
Enter name:</label>
<input
type=
"text"
id=
"name"
/>
</div>
<div>
<label
for=
"age"
>
Enter age:</label>
<input
type=
"text"
id=
"age"
/>
</div>
</form>
<button
id=
"set"
>
Set data</button>
<button
id=
"get"
>
Get data</button>
<p>
Data:</p>
<div
class=
"data"
>
<ul
id=
"data-list"
></ul>
</div>
<script
type=
"module"
src=
"idb-discussion.js"
></script>
</body>
</html>
And the idb-discussion.js
file:
import
{
openDB
}
from
'https://unpkg.com/idb?module'
;
(
async
()
=>
{
// open the database and create the data store
const
database
=
await
openDB
(
'ReaderNames'
,
1
,
{
upgrade
(
db
)
{
// Create a store of objects
const
store
=
db
.
createObjectStore
(
'reader'
,
{
keyPath
:
'id'
,
autoIncrement
:
true
});
// create new keys in the object store
store
.
createIndex
(
'age'
,
'age'
,
{
unique
:
false
});
store
.
createIndex
(
'name'
,
'name'
,
{
unique
:
true
});
}
});
async
function
setData
()
{
const
name
=
document
.
getElementById
(
'name'
).
value
;
const
age
=
document
.
getElementById
(
'age'
).
value
;
await
database
.
add
(
'reader'
,
{
name
,
age
});
}
async
function
getData
()
{
// get the reader data from the database
const
readers
=
await
database
.
getAll
(
'reader'
);
const
dataDisplay
=
document
.
getElementById
(
'data-list'
);
// add the name and age of each reader in the db to the page
readers
.
forEach
(
reader
=>
{
const
value
=
`
${
reader
.
name
}
:
${
reader
.
age
}
`
;
const
li
=
document
.
createElement
(
'li'
);
li
.
appendChild
(
document
.
createTextNode
(
value
));
dataDisplay
.
appendChild
(
li
);
});
}
document
.
getElementById
(
'set'
).
onclick
=
setData
;
document
.
getElementById
(
'get'
).
onclick
=
getData
;
})();
I won’t go into the full API, but highly recommend consulting the library’s documentation and using IDB whenever working with IndexedDB.
52.14.150.55