Persistent JavaScript Variables With Local Storage

08 Mar 2013

Recently I needed to create a multipage Rails app, which took a list of articles and sorted through them, then let you click a button that added the title to a preview list that could be temporarily saved and opened in a new window.

You can view an early backbone of the tool here to get an idea of where I was headed with the backend.

In terms of the preview function though, it was for an in house tool so it could be done on the front end without having to worry to much about tampering. Because of this I decided to write it in JavaScript and avoid having to waste server work on more Ruby functions when I could just let the browser handle it.

I started with a jQuery click function that added the Ruby variable to a JavaScript array.

show.html.erb

<%= javascript_tag do %>
  var t = '<%= @article.title %>';
  $(document).ready(function(){
    $('.add').click(function() {
      var cg = cln(t);
      saveState(cg);
    })
  })
<% end %>

The first function that is ran after the click is cln(), which uses Steven Livithan's all-around trim to clean up white space.

preview.js

    function cln(c) {
      return c.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
    }

At first the saveState() function simply pushed the variable into an array that was originally declared, something like this.

    var prevArray = [];

    function saveState(p) {
      prevArray.push(p);
    }

Unfortunately, this being a multipage app, the array would be lost every time the page would be changed or refreshed. This meant that unless all of the articles were added from the index page and the preview was opened from that same page, the preview would be pointless.

To deal with this I had a few options.

  • Cookies

Adding the variable to a cookie would let me store the variables per session. Unfortunately this product needed to be able to run where cookies were disabled, so that wasn't an option here. Even if it was allowed, the small (4kb) space of a cookie, the inclusion of cookies in every HTTP request, and the fact that this only needs to be session-temporary makes this an avoidable option.

  • Send it to a database

This is what I wanted to avoid from the beginning, so no go here.

  • Use localStorage

Local Storage is one of those great new additions to HTML5 that makes difficult, annoying problems much much easier to handle. You can find a great write up on the subject here. Allowing for more storage and perfect for temporary information, it can even be specified easily with Modernizr with a simple conditional.

    if (Modernizr.localstorage) {
        // localstorage stuff
    } else
        // workaround
    }

This wasn't much of an issue for me, as localStorage is supported in IE8+ and my project wouldn't be used on anything older.

Local Storage is used in the following syntax

    var foo = localStorage.getItem("bar");
    // code
    localStorage.setItem("bar", foo);

Conveniently, it can also be written as an associative array, which is a more helpful syntax for my situation.

    var foo = localStorage["bar"];
    // code
    localStorage["bar"] = foo;

Incorporating this into my preview while adding some checks for an empty localStorage array, saveState() function ended up like this.

    function saveState(p) {
      if (localStorage.prevArray == undefined || localStorage.prevArray == "") {
        prevArray.push(p);
        localStorage.prevArray = JSON.stringify(prevArray);
      } else {
        var pr = JSON.parse(localStorage.prevArray);
        pr.push(p);
        localStorage.prevArray = JSON.stringify(pr);
      }
    }

Local Storage can only handle strings, so they must be stringified and parsed, respectively, in order to store arrays. The conditional checks for an empty or undefined array and creates a new one if there wasn't one to begin with.

I'm sure this could be written more cleanly, but a quick and dirty solution was all that was needed.

Because I wanted to keep that array there but be able to clear and repopulate without necessarily wiping the entire localStorage, I wrote a small function that empties the array when a Clear button is clicked.

    function cr() {
      var r = JSON.parse(localStorage.prevArray);
      r = [];
      localStorage.prevArray = JSON.stringify(r);
    }

To note, you can complete remove an item from localStorage with removeItem(key), but I only needed to the array to be emptied.