All-in-one cookie function

Users stumbling across jQuery may notice the API’s designed so a method’s behaviour varies depending on the number and type of arguments passed in a call (have a look at the jQuery method!). In the right hands, this flexibility produces clean and elegant code without burdening the developer with 101 new method names to learn.

Let’s do the same for cookies (source: cookie.js).

Three into one does go

Googling for ‘javascript cookie functions‘ brings back Peter-Paul Koch’s trio of functions from QuirksMode. The functions are named setCookie, readCookie and eraseCookie. Browsing through the next few search-results we see the same thing going by different names: get, set and deleteCookie; add & remove, erase & delete, read, get and check – all variations on a theme, all separating functionality into a trio of functions.

Let’s look at the method signatures:

  • createCookie(name, value, days)
  • readCookie(name)
  • eraseCookie(name)

It’s pretty basic stuff.

To merge the three functions into one, we have to differentiate between reading, writing and deleting a cookie by the number and value of arguments; I’ve chosen an implementation where deleting a cookie is achieved by setting it to null.

Here’s some example usage:

  // create a cookie:
  cookie('name', 'value');

  // read it:
  alert(cookie('name'));

  // erase it:
  cookie('name', null);

Flexibility

All those cookie calls pass at least one parameter… but isn’t there something useful we can do with a plain parameterless cookie() call? Of course there is – let’s return an associative array of all cookie values!

By default (without specifying an explicit expiry time) cookies survive until you restart the browser. It’s kinda mandatory to provide some way of specifying an expiry time.

We’ll accept an optional third parameter specifying an expiry time in days:

  // create cookie for 1 year
  cookie('theme', 'minimal', 365);

  // grab all cookies:
  var cookies = cookie();
  alert(cookies.theme);

Looking good so far. But there’s more.

Like several of the cookie libraries, our function defaults to setting cookies on the top-level path “/” – this is a more common requirement than the browsers default behaviour, which sets a cookie so it’s only used at or below the current page level (i.e. a cookie set while looking at “/products/Nintendo-DSi-Console_Black/978372” wouldn’t be available when looking at any other product.)

To give developers flexibility I’ve made path another optional parameter – it could be used to share cookies between “/product/*” pages, but withhold them from any other area of a site.

While we’re on the subject of sharing – what about cross-domain cookies? Cookies are naturally assigned to the domain the page is being viewed on, but sometimes we want to make sure a cookie’s available to all sub-domains too. domain is also an optional parameter.

Having both path and domain as optional parameters could be confusing – after all, they’re both strings. Fortunately, we know paths begin with ‘/’ and domains don’t – and if you want to specify both you just stick to the right order: path then domain:

  // share cookie between product pages:
  cookie('view-description', 'hidden', '/products');

  // share cookie between domains:
  cookie('id', '_', '.ewelike.com');

  // save preferences cross-domain for 1 year:
  cookie('prefs', '_', 365, '/products', '.ewelike.com');

Finally, there’s an optional ‘secure’ parameter. I’ve never used this myself, but it’s there if you want it. It’s handy if you’re storing sensitive information in cookies and you only want to allow the browser to transfer the cookie value when it’s request pages via https. The code will take any truthy value hanging off the end of the parameters (anything that’s not been interpreted as expiry date, path or domain.):

  // set a secure cookie on the default path and domain
  // (expires when the browser closes)
  cookie('name', 'value', true);

Want the code? Grab it now… download cookie.js (dual-licensed under MIT and GPL, exactly the same as jQuery)

Posted Monday, March 9th, 2009 under JavaScript.

15 comments

  1. Wow you got huge fonts here!

    Nice idea about cookie(), thanks for sharing :)

    I’ve got a question: could I pass more options toghether when creating a cookie? e.g. both duration and path ?

    Thanks.

  2. Hi Francesco,

    Of course…

    I guess I made things a bit confusing by using ‘_’ instead of ‘cookie value’ in the article. All you have to do is:

    cookie('name', 'value', duration, '/path');
    

    Oh, and the font-size you see depends on how big your browser window is – the fonts scale-up or down so the average line-length stays around ~60-70 characters without leaving masses of white-space around. (There should be a link in the page footer to the theme’s creator – I believe their goal was to optimize readability, even if it does produce scarily big fonts on very wide screens…)

    Ash

  3. I’m on a 1440×900 16:10, not so big screen. The computed font size is 20.7px :O it’s very good for readability.

    Thanks for the clarification about the function definition, I’m definitely gonna use this!

  4. This looks really cool! Though, i’m often frustrated by functions with long parameter lists since it’s so hard to remember the correct order. What if everything past the first two params was an object of options?

    cookie(‘prefs’, ‘_’, { expire: 365, path: ‘/products’, domain: ‘.ewelike.com’, secure: false });

    That way you can pass whatever optional parameters you’d like. Just my $.02.

  5. Yeah, apparently dojo.cookie (http://api.dojotoolkit.org/jsdoc/1.3.2/dojo.cookie) is exactly what I suggested. It even includes passing ‘null’ to delete the cookie.

  6. @Jon,

    I know exactly what you mean when you say it’s hard to remember parameter ordering. I’m a day-to-day PHP user and still struggle to remember whether something like in_array is ordered (needle, haystack) or (haystack, needle).

    I had a crack at adding object support to increase this cookie functions flexibility even further, but it started to get confusing. It’s easy enough to check if the last parameter is an object and pull the properties out of it, but I couldn’t decide how to handle any other parameters and it all became a bit of a mess.

    It should be easy to strip the name-spacing stuff out of the dojo.cookie source-code if you prefer to pass parameters as objects. And though you lose the parameterless cookie(), the dojo implementation has nice expires handling (accepting explicit Date objects as being able to specify expiry-time in days.)

    “Swings and roundabouts” spring to mind.

  7. I personally don’t like the jQuery style of packing several different logical actions into one function. On the plus side, you have only one function name to remember. On the minus side, you still have to refer to the docs to look up the parameters you need for whichever of the function’s purposes you currently need, and the function name is less descriptive. Finally, this kind of “overloading” can lead to code that performs brittle type checks on the parameters passed into the function (e.g. checking for an Array, which is a non-trivial task and prone to cross-frame issues), none of which is necessary.

  8. @Tim,

    I prefer accessors to pairs of getters and setters – but they’re not widely available in JavaScript implementations (does anything except Mozilla support them?)

    Implementing accessors by checking for function arguments leads to fairly intuitive code IMHO:

    var value = obj.property(); // get
    obj.property(value); // set

    Of course you’re right when it comes to heavily overloading functions – and that’s not something unique to JavaScript implementations – but I find it coding more fluid when you chuck a couple of parameters at a function and don’t have to worry about a bunch of place-holders.

    // specifying path and domain just to pad things out
    // to the expiry parameter:
    setCookie(name, value, '/', location.host, expiry);
    // versus
    cookie(name, value, expiry);
    
  9. Ash,

    Obviously a lot of this is down to personal taste, and I have vacillated about overloading myself, coming down mostly on the side of avoiding it, though not religiously. I’m probably being thick but I think I’m missing your point – why would having separate setCookie and getCookie functions mean needing to supply more parameters?

    Tim

  10. @Tim,

    I was stating a preference in response to your comment “overloading can lead to code that performs brittle type checks.”

    I certainly didn’t mean to imply setter/getter pairs require more parameters than accessor methods.

    Ash

  11. OK, I see now.

    I am constantly frustrated when designing APIs in JavaScript that lend themselves naturally to the kind of overloading you can do in languages like Java or C#. The options are:

    1. Write several functions with slightly different names to deal with all the possibilities. Pros: clean to implement. Cons: bloats the API, hard to use.

    2. One function with overloading. Pros: quite simple to use. Cons: still requires remembering/looking up combinations of arguments that may be passed, and requires code in the implementation to check number and types of the arguments.

    3. One function that accepts a single object whose properties are just the arguments the caller requires. Pros: easy to use, flexible. Cons: requires extra code in the implementation to check properties of the object passed in, introduces possibility of error by mistyping property names in the calling code.

  12. Hey Ash, there’s a little error in your doc – first line, says “cooke”. Just letting you know.

  13. Sorry, forgot to mention, the type’s in the file you’ve linked to: http://hexmen.com/js/cookie.js.

    Assuming you understood, but just making sure.

  14. Thanks Dan (and thanks for pointing out exactly where the problem was.) I’ve fixed that now.

    Your wee comment reminds me I should get off my butt and finish another blog post..