Native Javascript Ninjutsu: Cookies and Variables

Ninja kanji

Sensei Says (dark ages, to dojos, to disciplines)

Javascript used to be a dark and ancient art, looked down upon by many web developers as a dishonorable – even malicious – ‘copy and paste’ language. Macromedia’s Shockwave – which later became Macromedia Flash, which even later became Adobe Flash – pushed audio, video, and interactive motion graphics onto the web in a cross-browser compatible format that all but decimated the need and appeal for Javascript. What little Javascript community there was began to seriously dwindle and die out.

And then the frameworks came to rise: Dojo, Yahoo! UI Library, Google Web Toolkit, jQuery, Prototype, MooTools, and many more. With these powerful armies by its side, the Javascript community quickly grew and regained its honor, competing heavily with the fluid animation and complex, real-time interactivity that Flash had delivered for years.

Javascript now seems to be a strong, healthy, and widely accepted language, frequently used and relied upon by web developers across the land. Yet how many of today’s programmers can write pure native Javascript without the aid of a framework? How many can perform AJAX requests without a framework? And most importantly, how many can craft fully cross-browser compatible code without a framework? In order to not become dependent on the frameworks – and thus risk sliding backwards into the dark ages – we must maintain a wide variety of practices: these are the native Javascript disciplines.

Discipline 4: Chōhō (espionage)

Setting cookies with JS is a technique that I must frequently use in my front-end development missions. The quickest way to set a cookie is by giving it a name and a value:

document.cookie = 'test_cookie=test_value';

The code above is pretty self-explanatory, we set the document.cookie object to equal a name and a value. This is the minimum amount of information one needs to define when setting a cookie, though a cookie actually contains all the following details:

  1. Name
  2. Content/Value
  3. Domain/Host
  4. Expires
  5. Path
  6. Secure

When adding multiple parameters to the cookie we need to separate them with semicolons:

document.cookie = 'test_cookie=test_value; expires=Mon, 06 Sep 2010 11:38:29 GMT; path=/';

By default the cookie is set to expire at the end of the session, but in my work I typically want cookies to expire within one hour. I achieve this time restraint by doing the following:

var the_date = new Date();
var unix_time = the_date.getTime();
var expiration = unix_time + ( 3600 * 1000 );

the_date.setTime( expiration );

document.cookie = 'test_cookie=test_value; expires=' + the_date.toGMTString();

First I get the current date and time using the Date() object and set it to the_date variable. I then retrieve the Unix time version of the current date and time by using the getTime() method and save this to the unix_time variable. This stores the time in milliseconds so I can simply add one hour by doing some quick math (60 seconds * 60 minutes * 1000 to get milliseconds + the current Unix time = one hour from now). I then reset the_date variable’s time to that of the desired expiration. When we finally write the cookie itself we must make sure to use the GMT string to set the expiration in the proper format.

The domain parameter sets the domain or host on which the cookie is valid. If this isn’t explicitly set it’s default value will be the full domain of the page that executes the cookie-setting JS. If you would like to allow the cookie to be accessed on multiple subdomains then you must set the base domain using two periods:

document.cookie = 'test_cookie=test_value; domain=.testdomain.com';

It’s important to remember that for security reasons you cannot set a cookie to have a cross-domain value. For example, if your cookie is set using JS on www.testdomain.com then you cannot set the domain parameter to www.othertestdomain.com.

The path parameter is quite similar, as it’s also used to restrict where (on the server) the cookie is made available. By default the path is set to the root of the current domain (/), but it’s wise to explicitly set this yourself as some older browsers have been known to have problems if it isn’t set (such as Netscape).

document.cookie = 'test_cookie=test_value; path=/';

If you want to limit the cookie usage to a particular directory on your server you can explicitly set the path parameter:

document.cookie = 'test_cookie=test_value; path=/test_directory';

Lastly, the secure parameter allows you to specify whether or not the cookie needs to be accessed over secure (encrypted) connections:

document.cookie = 'test_cookie=test_value; secure=true';

Now let’s put it all together and set a cookie that has a name and a value, is accessible on all subdomains and directories of the current host but only over secure connections, and expires within one hour:

var the_date = new Date();
var unix_time = the_date.getTime();
var expiration = unix_time + ( 3600 * 1000 );

the_date.setTime( expiration );

document.cookie = 'test_cookie=test_value; domain=.testdomain.com; path=/; secure=true; expires=' + the_date.toGMTString();

That about covers how to set cookies, but obviously setting a cookie doesn’t help much if we don’t know how to read it’s value. Retrieving cookie data is a bit more complex when using purely native JS. You can use the document.cookie to return all the cookie values:

var cookies = document.cookie;
document.write( cookies );

Do that and you’ll notice that what you get is a string of cookie names and values separated by semicolons:

test_cookie=test_value;another_test_cookie=another_test_value;

This isn’t too helpful until we’ve parsed the information we want out of the string. What I typically do is use the match() method with a regular expression to store the cookie value into an array:

var cookie_array = document.cookie.match( '(^|;) ?cookie_name=([^;]*)(;|$)' );

if ( cookie_array )
{
    var cookie_value = cookie_array[2]
}

I won’t explain the regular expression I’ve used in great detail – as the topic of regular expressions could easily make up a whole series of blog articles by itself — but I will summarize by saying that it finds the value and stores it into the second element of an array. Once we set this array we must check if it actually exists before attempting to extract the cookie value. Note that this regular expression will only return the value of the first cookie whose name matches the cookie_name string, therefore if you have two cookies set with the same name you will not be able to retrieve both values using this method. This is a rare case and for that reason I’m not worried about the use of this technique.

To update a cookie you can simply rewrite it using the methods I’ve shown above, although it’s important to note that if you change the domain parameter of a cookie you will actually end up with two cookies. This is because the original is not automatically removed. The best way to remove cookies is to set their expires parameter to a past date, and all their other parameters to null (or an empty string). Even once you’ve done this the cookie may still appear to be available. Most web browsers need a restart to refresh (and delete) their cookie data completely.

Cookies are not the only way to store and retrieve user data, and often I’m sent out on a JS mission which involves extracting data from the page itself. It’s very common that third party scripts will save their variables outside of the global namespace and this makes these variables impossible to access directly. For example take the following HTML page:

<html>
<head>
</head>
<body>

   <script type="text/javascript">
        function set_variable()
        {
            var the_variable = 'the value';
        }

        set_variable();
    </script>

</body>
</html>

Lets pretend that the above HTML page belongs to our client. Imagine that the script tag and its contents were added to the client’s webpage by a third party developer and that we’re not able to doctor or edit them whatsoever. Further imagine that all we’re allowed to do is include our own script after theirs. Because the_variable is defined within the scope of the set_variable() function we’re completely unable to access its value directly. We can however use some DOM parsing tricks to retrieve the value:

<html>
<head>
</head>
<body>

    <script type="text/javascript">
        function set_variable()
        {
            var the_variable = 'the value';
        }

        set_variable();
    </script>

    <script>
        var scripts = document.getElementsByTagName( 'script' );

        for ( var index in scripts )
        {
            if ( scripts[index].nodeType === 1 )
            {
                var script_code = scripts[index].innerHTML;
                parsed_code = script_code.match( "the_variable = '(.*?)'" );

                if ( parsed_code !== null )
                {
                    the_value = parsed_code[1];
                }
            }
        }
    </script>

</body>
</html>

First we store all the script tags from the DOM into an object called scripts. Then we run a for loop on this object, checking if each node is an element (nodeType === 1). If the node is an element then we proceed by storing its innerHTML to a string called script_code and then use the match() method to run a fairly straight forward regular expression on the string. If the results aren’t null then we store the found value to our final variable. Be aware that it may be necessary to slightly modify the regular expression pattern used to match different situations (such as different variable names, single quotes versus double quotes around the value, spaces before and after the equals sign, et cetera).

I hope this article has been helpful in providing you with a way to use native JS to deal with cookies and non-global variables in your scripts. Stay tuned for next week’s discipline: removeNode vs. removeChild.

Previous Disciplines
Discipline 1: AJAX with XHR
Discipline 2: Dynamic JS with PHP
Discipline 3: Include External JS

Resources
The Unofficial Cookie FAQ
W3Schools’ Javascript Cookies Tutorial

Are you smart? Innovative? Driven? If you’re interested in working on challenging projects in one of the world’s most fast-paced industries, why not check out the openings on our Careers page?