Chapter 3. Data Persistence

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.

3.1 Persisting Information with Cookies

Problem

You need to read or set the value of a browser cookie.

Solution

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

Discussion

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: 10px;
      }

      .data {
        width: 200px;
        background-color: yellow;
        padding: 5px;
      }
    </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.

3.2 Using sessionStorage for Client-Side Storage

Problem

You want to easily store information for a single session, without running into the size and cross-page contamination problems associated with cookies.

Solution

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

Discussion

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.

Example 3-1. Comparing sessionStorage and cookies
<!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: 10px;
      }

      .data {
        width: 100px;
        background-color: yellow;
        padding: 5px;
      }
    </style>
    <title>Comparing Cookies, localStorage, and sessionStorage</title>
  </head>
  <body>
    <h1>Comparing Cookies, localStorage, and sessionStorage</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>Session:</p>
    <div id="sessionstr" class="data"></div>
    <p>Local:</p>
    <div id="localstr" class="data"></div>
    <p>Cookie:</p>
    <div id="cookiestr" class="data"></div>

    <script src="cookie.js"></script>
    <script src="app.js"></script>
  </body>
</html>

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.

jscb 1502 3e
Figure 3-2. Displaying stored sessionStorage and cookie data in second tab

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.

See Also

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.

3.3 Creating a localStorage Client-Side Data Storage Item

Problem

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.

Solution

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

Discussion

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.

Example 3-2. Using localStorage to back up form entries in case of page reload or browser crash
<!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 Also

See Recipe 3.2 for more on the Storage object, and on sessionStorage and localStorage.

3.4 Persisting Larger Chunks of Data on the Client Using IndexedDB

Problem

You need more sophisticated data storage on the client than what’s provided with other persistent storage methods, such as localStorage.

Solution

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.

Example 3-3. Example of using IndexedDB to create a datastore, add data, and then retreive a data object
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}`);
    };
  };
};

Discussion

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

3.5 Simplifying IndexedDB with a library

Problem

You’d like to work with IndexedDB in an asynchronous fashion, using JavaScript promises.

Solution

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);
  });
})();
Note

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.

Discussion

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.150.55