JavaScript
Article

Quick Tip: Persist Checkbox Checked State after Page Reload

By James Hibbard

This quick tip describes how to have your browser remember the state of checkboxes once a page has been refreshed or a user navigates away from your site to come back at a later date.

It might be useful to Persist Checkbox Checked State if, for example, you use checkboxes to allow your users to set site-specific preferences, such as opening external links in a new window or hiding certain page elements.

For the impatient among you, there’s a demo of the technique at the end of the article.

The Checkbox Markup

So, the first thing we’ll need are some checkboxes. Here are some I made earlier:

<div id="checkbox-container">
  <div>
    <label for="option1">Option 1</label>
    <input type="checkbox" id="option1">
  </div>
  <div>
    <label for="option2">Option 2</label>
    <input type="checkbox" id="option2">
  </div>
  <div>
    <label for="option3">Option 3</label>
    <input type="checkbox" id="option3">
  </div>

  <button>Check All</button>
</div>

You’ll notice that I’ve included a Check All button to allow a user to select or deselect all of the boxes with one click. We’ll get to that later.

You’ll hopefully also notice that I’m using labels for the text pertaining to each of the boxes. It goes without saying that form fields should have labels anyway, but in the case of checkboxes this is particularly important, as it allows users to select/deselect the box by clicking on the label text.

Finally, you’ll see that I’m grouping the labels and check boxes inside block-level elements (<div> elements in this case), so that they appear beneath each other and are easier to style.

Responding to Change

Now let’s bind an event handler to the checkboxes, so that something happens whenever you click them. I’m using jQuery for this tutorial although, of course, this isn’t strictly necessary. You can include it via a CDN thus:

<script src="https://code.jquery.com/jquery-2.2.3.min.js"></script>

Now the JavaScript:

$("#checkbox-container :checkbox").on("change", function(){
  alert("The checkbox with the ID '" + this.id + "' changed");
});

Here I’m using jQuery’s psuedo-class :checkbox selector, preceded by the ID of the containing <div> element (checkbox-container). This allows me to target only those checkboxes I am interested in and not all of the checkboxes on the page.

Persist Checkbox Checked State

As you’re probably aware HTTP is a stateless protocol. This means that it treats each request as an independent transaction that is unrelated to any previous request. Consequently if you check a checkbox then refresh the page, the browser has no way of remembering the checkbox’s prior state and—in the example above—will render it in its unchecked form (although some browsers will cache the value).

To circumvent this, we’re going to store the checkbox states on a user’s computer using local storage (which is part of the Web Storage API). It has pretty good browser support:

Can I Use namevalue-storage? Data on support for the namevalue-storage feature across the major browsers from caniuse.com.

To modify the localStorage object in JavaScript, you can use the setItem and getItem methods:

localStorage.setItem("favoriteVillan", "Dr. Hannibal Lecter");

console.log(localStorage.getItem("favoriteVillan"));
=> Dr. Hannibal Lecter

To remove an item from local storage, use the removeItem method.

localStorage.removeItem("favoriteVillan");

console.log(localStorage.getItem("favoriteVillan"));
=> null

You can read more about local storage here: HTML5 Web Storage

With respect to our code, it seems that as we want to persist all of the checkboxes, a logical choice would be to make key/value pairs consisting of the checkbox IDs and their corresponding checked state. We can save these in an object literal which we can add to local storage. However, as local storage can only handle key/value pairs, we’ll need to stringify the object before storing it and parse it upon retrieval.

Let’s look at how we might do that:

var checkboxValues = JSON.parse(localStorage.getItem('checkboxValues')) || {};
var $checkboxes = $("#checkbox-container :checkbox");

$checkboxes.on("change", function(){
  $checkboxes.each(function(){
    checkboxValues[this.id] = this.checked;
  });
  localStorage.setItem("checkboxValues", JSON.stringify(checkboxValues));
});

Notice the logical OR operator (||) which returns the value of its second operand, if the first is falsy. We’re using this to assign an empty object to the checkboxValues variable, if no entry could be found in local storage. This is effectively the same as writing:

var checkboxValues = JSON.parse(localStorage.getItem('checkboxValues');
if (checkboxValues === null){
  checkboxValues = {};
}

Also notice that we are caching a reference to the checkboxes, so that we don’t have to keep querying the DOM. This is overkill in such a small example, but is a good habit to get into as your code grows.

The last thing to do is to iterate over the checkboxValues variable when the page loads and set the value of the boxes accordingly:

$.each(checkboxValues, function(key, value) {
  $("#" + key).prop('checked', value);
});

And that’s it. We now have persistent checkboxes.

See the Pen xVevNw by SitePoint (@SitePoint) on CodePen.

If you try refreshing the Code Pen (by clicking the RERUN button), you’ll see that the checkboxes remain selected, whereas if you enter something into the text input, that will vanish on reload.

Checking and Unchecking All the Boxes

To round this off, let’s implement the Check All button, so that users can select or deselect everything in one go.

When clicked, this button should check all of the checkboxes. When all of the checkboxes are checked its text should change to Uncheck All and its state should be stored in local storage, too.

Let’s start off like this:

var $button = $("#checkbox-container button");

function allChecked(){
  return $checkboxes.length === $checkboxes.filter(":checked").length;
}

function updateButtonStatus(){
  $button.text(allChecked()? "Uncheck all" : "Check all");
}

$checkboxes.on("change", function(){
  ...
  updateButtonStatus();
});

This will cause the button’s text to change when all of the check boxes are in a checked or unchecked state.

The only thing that might be slightly unusual here is the use of a ternary conditional in the updateButtonStatus method. This is a shortcut for an if statement and is the equivalent to:

if(allChecked()){
  $button.text("Uncheck all");
} else {
  $button.text("Check all");
}

Now a function to do the selecting and deselecting:

function handleButtonClick(){
  $checkboxes.prop("checked", allChecked()? false : true)
}

$("button").on("click", function() {
  handleButtonClick();
});

Finally, we’ll add a new value to the object in local storage, which I have renamed to reflect the fact that it now stores different types of values. I have also moved the logic for updating local storage into its own function:

var formValues = JSON.parse(localStorage.getItem('checkboxValues')) || {};

function updateStorage(){
  $checkboxes.each(function(){
    formValues[this.id] = this.checked;
  });

  formValues["buttonText"] = $button.text();
  localStorage.setItem("formValues", JSON.stringify(formValues));
}

And on page load:

$button.text(formValues["buttonText"]);

Demo

Here’s what we end up with.

See the Pen grJYvd by SitePoint (@SitePoint) on CodePen.

Conclusion

In this tutorial I have demonstrated how to persist form values to local storage and how to implement Check/Uncheck All functionality for checkboxes in a simple form.

If you have any questions or comments, I’d be glad to hear them below.

  • James Walker

    How is this a “quick tip?” Besides hardly being something that’s going to be widely used, it’s also no shorter than a typical tutorial, not helped by the random anecdotes and trivia on basic logical/ternary operators… Not often I feel obliged to criticise a SitePoint work!

    • James Hibbard

      Lol, a bit harsh, but thanks anyway.

      How is this a “quick tip?”

      Its length, for one thing—it’s less than half the length of a regular tutorial. It also demonstrates a basic technique (persisting data on the client) than can be referred back to from other articles.

      hardly being something that’s going to be widely used

      It’s a topic that pops up in our forums from time to time. A google search would also suggest that this is something people want to do.

      not helped by the random anecdotes and trivia

      Fair enough.

  • r smiles

    Great ‘Quick Tip’ James Hibbard! It was exactly what I needed for the app I’m creating. Loved the explanation (and anecdotes and trivia).

    • James Hibbard

      Thank you :) I appreciate you taking the time to comment.

  • http://sbkn.ru Alexander Sobakin

    Hello, James. Thank’s for your ultimate tutorial.
    Can you please tell how to apply this code not to a certain page, but to a site as a whole? I’ve got a lot of typical article pages with the same layout, which change due to a checkbox status and I want to make it possible for a user to adjust the view only one time.

    • James Hibbard

      Hey, sorry for the delayed response (vacation).
      To have this apply to any checkbox, just make the selector more generic:
      $checkboxes = $(":checkbox");
      Does that help?

  • Keir

    hey man can you help me with this
    function checkAll(id) {
    var list = document.getElementsByClassName(id);
    var all = document.getElementById(id);

    var checkboxValues = JSON.parse(localStorage.getItem(‘checkboxValues’)) || {};

    if (all.checked) {
    for (i = 0; i < list.length; i++) {
    list[i].checked = 1;
    checkboxValues[list[i]] = this.checked;
    }
    localStorage.setItem("checkboxValues", JSON.stringify(checkboxValues));
    $.each(checkboxValues, function(key, value) {
    $("#" + key).prop('checked', value);
    });
    } else {
    all.checked;

    for (i = 0; i < list.length; i++) {
    list[i].checked = 0;
    }
    }
    }
    it's not working

    • James Hibbard

      I don’t mind taking a look, but could you provide some code I can run to reproduce your problem (the above is misisng the HTML, for example). Also, can you be a bit more specific than “not working”

  • adamdesign

    Hey James! Great tutorial.
    Is there any to incorporate this into AJAX pagination? Be able to flip through pages while still keeping your selections?

    • James Hibbard

      Thanks :)

      Yeah, that should work, you’d just need to update the checkboxes’ checked state when the Ajax request completes and the new content is inserted into the page (presuming you are loading the checkboxes via Ajax, too).

      If you’d like some help getting that to work, head over to the SP forums, post a question in the JS forum, then tag me (@Pullo)

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in JavaScript, once a week, for free.