Serializing and deserializing cookies

Despite all the advances made in HTML5, browsers still have a very strange cookie API. The way it works is error-prone and inconsistent with the normal semantics of JavaScript.

The global document object has a cookie property, if a string is assigned to it, it magically adds the specified cookie to the list of cookies. When an attempt to read the cookie is made, a different value containing all the cookies is returned.

This API is not very useful without a wrapper. In this recipe, we're going to wrap this API in a wrapper that actually makes sense. We're going to test this wrapper by making a form page that saves itself on every modification (preserving the data after a page reload) for two minutes.

Getting ready

Let's find out how document.cookie works. We can set a cookie as follows:

document.cookie = "name=test; expires=Fri, 18 Jan 2023 00:00:00 GMT; path=/";

This sets a cookie for the whole domain of the current website called test, expiring on January 18 2023. Now if we try to read from document.cookie we will get "name=test", which means that all the extra data has been stripped out. If we continue by adding another cookie:

document.cookie = "otherName=test; expires=Fri, 18 Jan 2023 00:00:00 GMT; path=/"

And then try to access document.cookie, we get both cookies:

"name=test; otherName=test"

To actually clear the cookie we will need to set the expires date in the path as follows:

document.cookie = "otherName=test; expires=Fri, 18 Jan 2000 00:00:00 GMT; path=/"

And then we're back to document.cookie containing "name=test".

Finally, if we omit the expires date, we're going to get a cookie that lasts until the user closes the browser or until we clear it by setting its expire date in the past:

document.cookie = "otherName=test; path=/"

But what happens if the value contains the character ;? The cookie value will be cut off at this character and the next parameter (expire date or path) will be ignored. Fortunately, we can work around this by using encodeURIComponent to encode the value.

Now we have enough information to write our cookie handling library.

How to do it...

Let's write the code:

  1. Create the form page in index.html, it will contain three text fields and include our cookie wrapper script and formsaving script:
    <!DOCTYPE HTML>
    <html>
    <head>
    <title>Cookie serialization</title>
    </head>
    <body>
    <form method="post">
        <input type="text" name="text1" value="Form data will be saved"><br>
        <input type="text" name="text2" value="in the cookie formdata"><br>
        <input type="text" name="text3" value="and restored after reload">
    </form>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
    <script type="text/javascript" src="cookie.js"></script>
    <script type="text/javascript" src="example.js"></script>
    </body>
    </html>
  2. Create cookie.js, which implements and exports the cookie API. It will have the following function:
    • cookie.set(name, value, options): This function sets the value of a cookie. The value can be an arbitrary object as long as it can be serialized by JSON.stringify. Available options are expires, duration, and path.
      (function(exports) {
      
          var cookie = {};
      
          cookie.set = function set(name, val, opt) {
            opt = opt || {};
            var encodedVal = encodeURIComponent(JSON.stringify(val)),
            expires = opt.expires  ? opt.expires.toUTCString()
            : opt.duration ? new Date(Date.now()
                                           + opt.duration * 1000).toUTCString()
            : null;
            
            var cook = name +'=' + encodedVal + ';';
            if (expires) cook += 'expires=' + expires;
            if (opt.path) cook += 'path=' + opt.path;
            document.cookie = cook;
          };
      
          cookie.del = function(name) {
            document.cookie = name + '=deleted; expires='
            + new Date(Date.now() - 1).toUTCString();
          }
          cookie.get = function get(name) {
            var cookies = {};
            var all = document.cookie.split(';').forEach(function(cs) {
            var c = cs.split('='),
            if (c[1])
            cookies[c[0]] =
            JSON.parse(decodeURIComponent(c[1]));
            });
          if (name)
            return cookies[name]            
            else
            return cookies
          };
      
          exports.cookie = cookie;
      }(typeof(exports) !== 'undefined' ? exports : this));
  3. Create example.js to test the new cookie API. It loads the form data when the document loads and saves it when it's changed:
    $(function() {
        var savedform = cookie.get('formdata'),
        savedform && savedform.forEach(function(nv) {
          $('form')
          .find('[name="'+nv.name+'"]')
          .val(nv.value);
        });
        $('form input').on('change keyup', function() {
          cookie.set('formdata', $('form').serializeArray(),
          {duration: 120});
        });
    });

How it works...

Our API implements several convenient functions to deal with cookies.

The cookie.set function allows us to set a cookie. It takes three parameters: name, value, and options.

The value is serialized with JSON.stringify, and then encoded using encodeURIComponent. As a result we can store any object that can be serialized with JSON.stringify (there are however size limits which vary between browsers).

The options parameter is an object which can contain three properties: expires, duration, and path. The expires property is the date when the cookie should expire. Alternatively, duration can be provided – it is the duration that the cookie should last in seconds. If both of these are omitted, the cookie will last until the end of the current browser session. Finally, the path property is a string specifying the path where the cookie is available. The default is the current path.

There's more...

Cookies should not be used to store large amount of data. Most browsers limit the cookie size at 4 KB per cookie. Some browsers limit the total size of all cookies to 4 KB. Data stored inside cookies is transferred with every request made to the server, increasing the total use of bandwidth.

For larger data we can use local storage instead. More information can be found in Chapter 10, Data Binding Frameworks.

Note that this example doesn't work when opened on a local filesystem. To make it work, a local HTTP server must be run. See appendix for more information on how to run a simple HTTP server.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.55.193