Today I’ll be going over how to build a small Bookmarker app using pure javascript. I won’t be using any javascript libraries save for bootstrap. I was inspired to write this article by Traversy Media whose video goes over the same thing and in fact you could just as well watch that if you learn better through video. I will be extending his approach by adding validation for cases where a bookmark name or url might be added twice. That being said, let’s get right into it.

Approaching the problem

To create this app, we’ll have to write three main function:

  • A saveBookmark function: This function will check whether there are any existing bookmarks. If there aren’t any, it will create a new array in the localStorage and then save the bookmark. If there are, it will simply push the bookmark into the array. It will then refresh the UI and display the added bookmark.
  • A fetchBookmarks function: This function will loop over our localStorage array and display the items according to the UI logic we provide.
  • A deleteBookmark function: This function is self-explanatory. It checks the selected item against the corresponding value in the localStorage array and deletes that value. Then it refreshes the UI to show that the bookmark was deleted.

Creating the UI

For the UI, we’ll create a simple form using bootstrap and then create a div to house the added bookmarks below the form. You could design it in many ways, but I’ll take a similar approach as that in Traversy Media’s video.

Dependencies

I’ll be using bower to install the dependencies. If you don’t have it installed simply do so in your bash console like this:

sudo npm install bower
  • Bootstrap
bower install bootstrap
  • jQuery
bower install jquery

Designing the Form

Basic HTML structure
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Bookmarker</title>
    <link href="bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="css/style.css" rel="stylesheet">
  </head>
  <body>
    <div class="page-wrapper">
      <h2>Javascript Bookmarker</h2>
    <!--- Form goes here -->

    <!--- Fetched bookmarks are displayed here-->
    </div>
    <script src="bower_components/jquery/dist/jquery.min.js"></script>
    <script src="bower_components/bootstrap/dist/bootstrap.min.js"></script>
  </body>
</html>
The Form UI
<form id="myForm">
<div class="form-group">
  <label>Site Name</label>
  <input class="form-control" type="text" id="siteName" placeholder="Site Name">
</div>
<div class="form-group">
  <label>Site Url</label>
  <input class="form-control" type="text" id="siteUrl" placeholder="Site Url">
</div>
<button type="submit" class="btn btn-primary">Save Bookmark</button>
</form>
Fetched Bookmarks
<div class="row">
  <h3>Saved Bookmarks</h3>
  <div class="col-lg-12" id="savedBookmarks"></div>
</div>
The CSS
.page-wrap{
  max-width:768px;
  margin: 0 auto;
  padding: 1em;
}

#myForm{
  padding: 1em;
  background-color: #f7f7f7;
}

/* this one is for later when we add bookmarks to our UI*/
.card{
  margin-bottom: 1em;
}
.row{
  margin: 0 !important;
  padding: 1em !important;
}

The result

UI result

Add Event listener

Great, so that’s about it for the UI. Let’s start working on the form functionality. The first thing we’ll need to do is listen for the submit event and we do that by adding an event listener method to our form element. The method will take two arguments: the first is the event and the second is the action that will be triggered by the event.

// Listen for form submit

document.getElementById('myForm').addEventListener('submit', saveBookmark);

The saveBookmark Function

The action taken when the submit event is caught is to save a bookmark. This will be handled by a saveBookmark function.


function saveBookmark(e){
  // declare variables to store the values of the respective input elements
  var siteName = document.getElementById('siteName');
  var siteUrl = document.getElementById('siteUrl');

  // create object to store the values from the input elements.
  var bookmark = {
    name: siteName,
    url: siteUrl
  }

  // The next thing is to check if localStorage contains any bookmarks
  // if not, we create the bookmark array and write to it

  if(localStorage.getItem('bookmarks') == null){
    var bookmarks = [];
    bookmarks.push(bookmark);

    // now write to localStorage.
    localStorage.setItem('bookmarks', JSON.stringify(bookmarks));
  } else {

    //pull the existing bookmarks from localStorage
    var bookmarks = JSON.parse(localStorage.getItem('bookmarks'));
    bookmarks.push(bookmark);
    localStorage.setItem('bookmarks', JSON.stringify(bookmarks));
  }

  // a crucial thing to do at this point is to
  // prevent the default action of the submit event
  e.preventDefault();
}

After writing to localStorage, nothing will happen to our UI so we’ll need a way to fetch the bookmarks and update our UI to display them in a neat way. So, you guessed it, we’ll write a fetchBookmarks function to handle this.

The fetchBookmarks function

function fetchBookmarks(){
  var bookmarks = JSON.parse(localStorage.getItem('bookmarks'));
  var savedBookmarks = document.getElementById('savedBookmarks');
  savedBookmarks.innerHTML = '';

  // Loop through our bookmarks object and display each bookmark
  for(var i = 0; i<bookmarks.length; i++){
    var name = bookmarks[i].name;
    var url = bookmarks[i].url;
    savedBookmarks.innerHTML =
    '<div class="card bg-light">' +
     '<div class="card-body"'
     '<h3>' + name +
     '<a class="btn btn-outline-dark" target="_blank" href="'+url+'">Visit</a>' +
     '<a class="btn btn-danger" href="#" onclick="deleteBookmark(\''+url+'\')">Visit</a>'+
     '</h3>' +
     '</div>'
  }
}

// After obtaining a bookmark, we display it in a card and add
// a button to visit the link or to delete it

Visiting a link is pretty straight forward, we simply follow the url but to delete it, we’ll need to identify the specific url in our localStorage and then remove it from our object. For that—yes you’re right once again—we’ll write a deleteBookmark function.

// we'll pass an argument of url to the function
function deleteBookmark(url){
  var bookmarks = JSON.parse(localStorage.getItem('bookmarks'));
  // let's then loop through our object to identify the matching url
  for(var i = 0; i<bookmarks.length; i++){
    // using the splice method, we remove the item
    // from our array of bookmarks. the second argument
    // states that we'll only be removing one item
    bookmarks.splice(i, 1);
  }
  // then we reset our data in localStorage
  localStorage.setItem('bookmarks', JSON.stringify(bookmarks));

  // after deleting, we refetch our bookmarks to update the UI
  fetchBookmarks();
}

We’re nearly done. One thing to do is to add a fetchBookmarks() function to our saveBookmarks function so that the bookmarks are there after every browser refresh. The second thing we’ll do is to add some validation to ensure we’re not saving things twice and that our form is not empty.

Validation

Back in our saveBookmarks function, add this after setting up the bookmark object

// if input is empty, alert the user
if(!siteName || !siteUrl){
   alert('Please fill in the form');
 }
// if a bookmark already exists, alert the user
 var bookmarks = JSON.parse(localStorage.getItem('bookmarks'));
 for(var i =0; i<bookmarks.length; i++){
   if(bookmarks[i].name ==siteName || bookmarks[i].url == siteUrl){
     alert('Bookmark already exists');
   }
 }

// just before the preventDefault method
// add fetchBookmarks and reset the form
document.getElementById('myForm').reset();
fetchBookmarks();

And there you have it.

UI Result

The UI could use some work but I’ll leave you to get creative with that. Another great addition to this app would be the edit function. It would produce a modal with a form to change the name or the url of our bookmark. I will extend this app in a future post to add that functionality.

FIN