Website logo

Tag: Javascript

How To Use Windows LocalStorage As A Make-Shift Database With Javascript

Honing the many facets of web development can be a challenge to beginners as it tends to take a significant amount of time and practice. However, it’s possible to write some Javascript code that can save your application data to a storage mechanism in the web browser without having to set up a database management system (DBMS).

What Is LocalStorage?

The Web Storage API contains a Storage interface which in turn provides access to two separate storage mechanisms called [localStorage[] and sessionStorage. With the localStorage mechanism, you can save data to your web browser as key/value pairs. What makes it distinct from the sessionStorage is the fact that your stored data neither expires nor disappears. It persists data across browser sessions until you intentionally delete them.

I will show you how to persist your front-end application data to the local storage of your browser by creating a simple Books Inventory app.

Create a Books Inventory Application

The coding process is going to be a bit longer because we have to create a large percentage of our DOM using JavaScript. All we need to do is create two files named index.html, and script.js in any convenient directory. A very minimal page styling will be handled by a Bootstrap 4 CDN link in the HTML document.

Follow this link to copy and paste its content into your index.html file. If you look in the body of the file, you will see a line with <div id="books-div" class="card-columns"></div>. It is the line where we’ll insert our dynamically created DOM elements.

Another thing you’ll notice is the presence of two forms. Both forms are meant to be hidden and can be toggled on and off when you have the JavaScript code for that functionality. The first form will be used to create or add new book items, while the second form will be used to update an existing book.

All JavaScript code from here on will go into script.js in the exact order.

script.js

// Get all relevant elements from the existing DOM
const formButton = document.getElementById('form-toggle');
const booksDiv = document.getElementById('books-div');
const addNew = document.getElementById('submit');
const editBook = document.getElementById('update-submit');

Since the second form is a popup modal, then it will be handled by Bootstrap. The toggling of the first form, although not a particularly relevant feature, will be handled using the form-toggle id.

If you look at the form we are working on, you will see all the required fields including the submit button. We need to create a div element that will contain and display all the input data received from those fields, as well as buttons for updating and deletion on the screen. A single entry should look like the following screen capture:

To achieve that display, we need to create a div element with Bootstrap’s card feature in a JavaScript function. It should look like this:

<div class="card bg-light text-center">
  <div class="card-header">
    <h2>Book Title</h2>
  </div>
  <div class="card-body">
    <h4 class="card-title">John Doe</h4>
    <h5 class="card-subtitle mb-2 text-muted">15<em> Copies</em></h5>
    <p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisci.</p>
    <button class="btn btn-primary">Edit Book</button>
    <button class="btn btn-danger">Remove Book</button>
  </div>
  <div class="card-footer text-muted">
    <h6>$50</h6>
  </div>
</div>

A function to create a div element

const createCard = (trackId, bookTitle, bookAuthor, bookPrice, bookQuantity, bookSummary) => {
  // Create elements and assign Bootstrap classes and Parameter values as attributes to them.
  let rootdiv = document.createElement('div');

  rootdiv.setAttribute('class', 'card bg-light text-center');
  rootdiv.setAttribute('id', trackId);

  let headerdiv = document.createElement('div');
  let bodydiv = document.createElement('div');
  let footerdiv = document.createElement('div');

  headerdiv.setAttribute('class', 'card-header')
  bodydiv.setAttribute('class', 'card-body')
  footerdiv.setAttribute('class', 'card-footer')

  let book = document.createElement('h3');
  let author = document.createElement('h5');
  let quantity = document.createElement('h6');
  let summary = document.createElement('p');

  author.setAttribute('class', 'card-title')
  quantity.setAttribute('class', 'card-subtitle mb-2 text-muted')
  summary.setAttribute('class', 'card-text');

  let update = document.createElement('button');
  let remove = document.createElement('button');
  let price = document.createElement('h6');

  update.setAttribute('class', 'btn-primary');
  update.setAttribute('data-target', '#updatebook');
  update.setAttribute('data-toggle', 'modal');
  update.setAttribute('data-editkey', trackId);

  remove.setAttribute('class', 'btn-danger');
  remove.setAttribute('data-deletekey', trackId);

  // Append textContent to elements.
  book.textContent = bookTitle;
  author.textContent = bookAuthor;
  quantity.textContent = bookQuantity + ' Copies';
  summary.textContent = bookSummary;
  update.textContent = 'Edit Book';
  remove.textContent = 'Remove Book';
  price.textContent = '$' + bookPrice;

  // Append elements to elements.
  headerdiv.appendChild(book);

  bodydiv.appendChild(author);
  bodydiv.appendChild(quantity);
  bodydiv.appendChild(summary);
  bodydiv.appendChild(update);
  bodydiv.appendChild(remove);

  footerdiv.appendChild(price);
  rootdiv.appendChild(headerdiv);
  rootdiv.appendChild(bodydiv);
  rootdiv.appendChild(footerdiv);

  booksDiv.appendChild(rootdiv);
}

Now that we have our div block element, the next function will handle the toggling of the first form

A function to toggle a form

const formToggle = () => {
  const form = document.getElementById('form-div');
  if (form.style.display === "none") {
    form.style.display = "block";
  } else {
    form.style.display = "none";
  }
}
formButton.addEventListener("click", formToggle);

Simply click on the Add New button to toggle the form on and off.

A function to add a Book object to Local Storage

const addBook = () => {
  let tracknum = document.getElementById('book-id').value;
  let bookttl = document.getElementById('book-title').value;
  let bookauth = document.getElementById('book-author').value;
  let bookprz = document.getElementById('book-price').value;
  let bookqty = document.getElementById('book-quantity').value;
  let booksmry = document.getElementById('book-summary').value;
  createCard(tracknum, bookttl, bookauth, bookprz, bookqty, booksmry);
  const bookStore = {
    id: tracknum,
    title: bookttl,
    author: bookauth,
    price: bookprz,
    quantity: bookqty,
    summary: booksmry
  }
  window.localStorage.setItem(tracknum, JSON.stringify(bookStore));    
}
addNew.addEventListener('click', addBook);

The tracknum value in the addBook function is very important because it is the number that will be used to identify the items in Local Storage. We will need it to update or delete an entry. 1. Call the createCard function to create a card div with the values received via the form. 2. Use window.localStorage.setItem(tracknum, JSON.stringify(bookStore)); to save the object with an id assigned to it.

The function is triggered when the form’s submit button is clicked.

A function to display all entries made to the Local Storage

const displayAll = () => {
  let fakeDb = localStorage
  let accessKeys = Object.keys(fakeDb);
  accessKeys.forEach((booksItem) => {
    let entry = JSON.parse(localStorage.getItem(booksItem))
    createCard(entry.id, entry.title, entry.author, entry.price, entry.quantity, entry.summary)
  })
}
window.addEventListener('load', displayAll)

Card divs are created for each of the entries in the store, while their display is triggered via the load event listener of the window object.

A function to remove an entry

const removeItem = (event) => {
  if(event.target.hasAttribute('data-deletekey')) {
    let targetKey = event.target.dataset.deletekey;
    localStorage.removeItem(targetKey);
    location.reload();
  }
}
window.addEventListener('click', removeItem);

In the createCard function, we used the remove.setAttribute('data-deletekey', trackId); line to assign the entry ID named trackId to an HTML data attribute. It now comes in handy to target a single entry by its ID to be removed. Remove the target with localStorage.removeItem(), then call location.reload() to immediately reflect the change on the browser.

A function to refill the update form with values to be edited

const refillForm = (event) => {
  if(event.target.hasAttribute('data-editkey')){
    let targetKey = event.target.dataset.editkey;
    let selected = JSON.parse(localStorage.getItem(targetKey));
    const selected_id = selected.id,
      selected_title = selected.title,
      selected_author = selected.author,
      selected_price = selected.price,
      selected_quantity = selected.quantity,
      selected_summary = selected.summary;

    // Transfer data to modal form
    document.getElementById('update-book-id').value = selected_id;
    document.getElementById('update-book-title').value = selected_title;
    document.getElementById('update-book-author').value = selected_author;
    document.getElementById('update-book-price').value = selected_price;
    document.getElementById('update-book-quantity').value = selected_quantity;
    document.getElementById('update-book-summary').value = selected_summary;
  }
}
window.addEventListener('click', refillForm);

The role of this refillForm function is to use the trackId assigned here update.setAttribute('data-editkey', trackId);to a data-attribute in the createCard function to check for an event.target.hasAttribute('data-editkey') on a selected entry. It then transfers the existing values to the update form. I made sure to disable the ID field so that it would not be changed. Modifying it would simply add a new entry instead of updating the one that was selected.

A function to save the edited fields to Local Storage

const updateBook = () => {
  let tracknum = document.getElementById('update-book-id').value;
  let bookttl = document.getElementById('update-book-title').value;
  let bookauth = document.getElementById('update-book-author').value;
  let bookprz = document.getElementById('update-book-price').value;
  let bookqty = document.getElementById('update-book-quantity').value;
  let booksmry = document.getElementById('update-book-summary').value;
  const updateBook = {
    id: tracknum,
    title: bookttl,
    author: bookauth,
    price: bookprz,
    quantity: bookqty,
    summary: booksmry
  }
  window.localStorage.setItem(tracknum, JSON.stringify(updateBook));
  location.reload();
}
editBook.addEventListener('click', updateBook);

By clicking on the submit button, the updateBook function saves the new information to the Local Storage.

You can get the complete code here.

One Last Thing

I’d like you to make an attempt at extending the app’s functionality by making it impossible to add multiple entries with the same ID. If you make an entry with an existing ID, it will simply update the information that was already there to a new one. Figure out how to prevent the Storage from accepting an entry with a duplicate ID.