How to Deal with Cookies in JavaScript

For years, many web developers have needed to store data on the client. Before the advent of HTML5 and its new mechanisms, every developer used cookies to achieve this goal. Unfortunately, working with cookies in JavaScript can cause a lot of headaches. This article discusses what cookies are, and how you can build functions to create, retrieve, and delete them.

What are Cookies?

A cookie is a piece of data which is sent from a website and stored locally by the user’s browser. Cookies are needed because HTTP is stateless. This means that HTTP itself has no way to keep track of a user’s previous activities. One way to create state is by using cookies.

To Use Cookies or Not to Use Cookies

Today most, if not all, websites worldwide use cookies. However, cookies are rather limited, as they can only store up to 4KB of data. Moreover, many developers claim that because cookies are sent to the server with each HTTP request, large ones can consume considerable network bandwidth, hurting performance. Another important criticism against cookies has been written by Remy Sharp in the co-authored book Introducing HTML5. This can be potentially catastrophic for mobile connections. Another important point to keep in mind is that if you have European visitors, then your site must comply with the EU E-Privacy Directive from May 26, 2012. If you never heard about this, take a look at Why Your Site is Now Illegal in Europe.

In recent years, a lot of thought has been put towards finding an alternative to cookies. Thanks to HTML5, some new techniques have appeared on the scene. The first one is Web Storage API, which has methods for storing name-value pairs. For an overview of the Web Storage API, I advise you to read An Overview of the Web Storage API. The second alternative is the Web SQL Database API, which stores data in databases that can be queried using a variant of SQL. While Web SQL is well supported, the standard is no longer being actively maintained. Last, but not least, is the Indexed Database API, that defines a database of records holding simple values and hierarchical objects. You can read more about IndexedDB in Up Close and Personal with HTML5 IndexedDB. Unfortunately, IndexedDB isn’t widely supported, so you probably shouldn’t rely on it.

No matter your preference, there are several important points to understand. While all of these APIs provide local storage similar to cookies, they do not send their data back to the server. So, in most cases you will use both cookies and one of the storage APIs. Technically, the same effects could be achieved using AJAX, but this over complicates the task and requires additional code.

How Cookies are Made

The structure of a cookie is really simple. It’s nothing but several key-value pairs. Pairs are separated by semicolons, while the equal sign character separates the key from its value. A cookie can optionally have an expiration date, after which it’s deleted. If an expiration date isn’t provided, the cookie will last until the session or browser is closed. If you set the expiration date in the past, the browser will delete the cookie. Please note that the format of the date is important, as it has to be in UTC/GMT. Moreover, you can specify a domain and a path where the cookie can be read and written. By default the path value is ‘/’, meaning that the cookie is visible to all paths in a given domain. If you don’t specify the domain, it will belong to the page that set the cookie. The way you set these data also matter. The order should be:

key-value;expiration_date;path;domain;.

The following example shows a cookie that is accessible in all the paths of the domain, and has just one key-value pair.

visits=3; path=/;

The following example shows a cookie that is accessible in all the paths of the domain (by default), and expires on October 31, 2012 at 11 a.m..

last-visit=Mon, 15 Oct 2012 19:36:00 GMT; expires=Wed, 31 Oct 2012 11:00:00 GMT;

Scripting Cookies

So far, I have explained what cookies are, as well as some of their pros and cons. Now it’s time to see what functions JavaScript exposes to work with them. Unfortunately, the first thing I have to say is that JavaScript doesn’t have native methods to easily work with cookies. JavaScript can create, retrieve, and delete cookies using the document.cookie property, but it’s not really a pleasure to use. Every time you are forced to deal with split(), substring() and for loops. Please note that while you can treat document.cookie like a string variable, it’s actually more than that. For example, take the following script:

document.cookie = "visits=3; path=/;";
document.cookie = "last-visit=Mon, 15 Oct 2012 19:36:00 GMT; expires=Wed, 31 Oct 2012 11:00:00 GMT;";

If you then print document.cookie, you’ll get an unexpected result as shown below:

console.log(document.cookie);
// print visits=3; last-visit=Mon, 15 Oct 2012 19:36:00

By now, you’ve seen the hardships of dealing with cookies in JavaScript. So, it’s time to create our own functions to easily manage them. The following function will help you to create a cookie. Please note that the expires parameter can be either an instance of a Date object or a number which indicates the number of days. The latter can be a negative number, which sets the expiration date in the past.

function createCookie(name, value, expires, path, domain) {
  var cookie = name + "=" + escape(value) + ";";

  if (expires) {
    // If it's a date
    if(expires instanceof Date) {
      // If it isn't a valid date
      if (isNaN(expires.getTime()))
       expires = new Date();
    }
    else
      expires = new Date(new Date().getTime() + parseInt(expires) * 1000 * 60 * 60 * 24);

    cookie += "expires=" + expires.toGMTString() + ";";
  }

  if (path)
    cookie += "path=" + path + ";";
  if (domain)
    cookie += "domain=" + domain + ";";

  document.cookie = cookie;
}

You can call this function as shown below.

createCookie("website", "audero.it", new Date(new Date().getTime() + 10000));
createCookie("author", "aurelio", 30);

Now that you set a cookie, you need to be able to retrieve them. You’ll do it using a given key and the following getCookie() function. It returns the value of the key if it’s found, and null otherwise.

function getCookie(name) {
  var regexp = new RegExp("(?:^" + name + "|;\s*"+ name + ")=(.*?)(?:;|$)", "g");
  var result = regexp.exec(document.cookie);
  return (result === null) ? null : result[1];
}

Using getCookie() is very simple. You simply pass the key as a parameter like as shown below.

console.log(getCookie("author")); // print aurelio
console.log(getCookie("nothing")); // print null

Now we need the last function to delete a cookie. The function shown is very simple because it relies on getCookie() to test if the given name is set, and createCookie() to set the expiration date in the past.

function deleteCookie(name, path, domain) {
  // If the cookie exists
  if (getCookie(name))
    createCookie(name, "", -1, path, domain);
}

Given that function, to delete a cookie you can simply write:

deleteCookie("author");
console.log(getCookie("author")); // now print null

Using the functions shown, you’ll be able to easily manage cookies. The web is also full of plenty of other cookie handling functions. Of the plethora of functions you can find, I’d like to show the ones written by Peter-Paul Koch (P.P.K.), a guru of front-end development, on quirksmode.com. I’d like to thank him for allowing me to include them in this article. P.P.K’s function to create a cookie is shown below.

function createCookie(name,value,days) {
  if (days) {
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
  }
  else var expires = "";
  document.cookie = name+"="+value+expires+"; path=/";
}

Similarly, to retrieve a cookie, use the following function.

function readCookie(name) {
  var nameEQ = name + "=";
  var ca = document.cookie.split(';');
  for(var i=0;i < ca.length;i++) {
    var c = ca[i];
    while (c.charAt(0)==' ') c = c.substring(1,c.length);
    if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
  }
  return null;
}

And this is the function to delete a cookie.

function eraseCookie(name) {
  createCookie(name,"",-1);
}

Conclusions

Throughout this article you learned what cookies are, how they are made, and their strengths and weaknesses. You’ve also seen how you can deal with cookies using custom functions. As I also pointed out in my previous article, JavaScript lacks some basic utility functions. Luckily, you can easily build your own or search the web to solve your problems.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Angelo

    great tutorial on cookies… Do not have experience with them but I will surely take a look at it when I get a chance. Thanks for sharing Aurelio.

    • http://www.audero.it/ Aurelio De Rosa

      I’m happy you think it’s useful. Continue reading us to know other useful stuff.

      • Angelo

        Will surely do. This site seems to be promising. :) Thanks.

  • http://www.brothercake.com/ James Edwards

    Instead of

    if (Object.prototype.toString.call(expires) === “[object Date]“)

    I’d suggest simply

    if(expires instanceof Date)

    Simpler, shorter, faster and cheaper.

    Though I don’t actually think you need that condition at all. You can test for function input and auto-correct ad-infinitum, but it’s essentially a waste of code to do stuff like that. You don’t check that the name is a string, or that value isn’t already escaped, or that path and domain are valid; so why not just assume the date is valid too?

    • http://www.audero.it/ Aurelio De Rosa

      Hi James and thank you for the comment. I added the test because while a already escaped value is anyway a valid value to add to a cookie, I don’t liked the idea of a bad date so, as you can see from the script, I use a default date. Anyway, while I agree with you saying that there are also other tests I might do, in my opinion made same test is better than no test at all.

  • http://www.brothercake.com/ James Edwards

    But surely, if the date were not valid it would throw an error over toGMTString, so the error-finding you want is already present elsewhere in the code.

    I think it depends on the use-case. If it’s just for you to use, you can trust yourself to give it valid input. But if it’s for others to use, then the conditions you have could actually be counter-productive, because they’ll silently fix broken input, rather than allowing an error to surface so the user can fix it properly.

    • http://www.audero.it/ Aurelio De Rosa

      You’re maybe right that fix user input errors could lead to problem. However, in my opinion, the point here is to manage errors versus raise errors. As you pointed out, don’t made the test would raise an error while doing the test I wrote will manage the error which is a lot better.

      • http://www.brothercake.com/ James Edwards

        I think that’s exactly the point yes, but I take the opposite view. It’s better to raise errors than manage them, in this (and similar) cases.

        Managing errors is about preventing end-users from seeing them. But the user of this function is another developer, not the end-user. If you manage their errors, then they won’t correct them, won’t even realise an error has occurred. But if you raise an error, it gets fixed, and then the error doesn’t exist. Which means the function runs faster because it has less work to do.

        Not a huge difference either way, for sure :-) But I think it comes down to an overall approach — similar to the whole strict vs loose thing — I think it’s better to be strict, because strictness produces better results, even if it is more painful in the process!

  • Wim

    Simple side note : the code examples are not shown correctly on an iPad. The right hand side is not visible when the code line exceeds the column width.

    • http://www.cjihrig.com Colin Ihrig

      Thanks for pointing this out!

  • Paul Vollmar

    Please consider a “printer-friendly” option. This is a good tip, but I may not need it for months. What I usually do is print the document to a pdf file, and then save it in a “Tips” folder. I cannot do this with your page without getting a pretty useless document in terms of ugly formatting.

    • http://www.cjihrig.com Colin Ihrig

      Hi Paul. I will pass this idea on to our development team.

  • Sheikh Heera

    The subject is not very new to me but I liked it because it’s nicely presented.