Tuesday, August 4, 2009

Handling JavaScript Cookies with Prototype

When dealing with cookies, it's important to be aware of certain browser limitations that might affect your user's experience. For example, Internet Explorer can only handle up to 20 cookies per domain at a time. If your site relies heavily on cookies, this could become a problem if the browser reaches its max and starts tossing cookies unexpectedly. In ColdFusion, this could mean losing your CFID and CFTOKEN cookies and getting logged out for no apparent reason.

To address this, I created a simple Cookie manager with the help of the Prototype library. The basic idea is to store multiple cookies in a single cookie using a JSON key-value pair associative array.

The methods available are:


<script type="text/javascript">
Cookie.get(key, default);
Cookie.set(key, value);
Cookie.clear(key);
Cookie.exists(key);
</script>


For example, I might have the following code to toggle the visibility of a menu:


<script type="text/javascript">
toggleMenu = function(set) {
var visible = Cookie.get('menu', true);
if (set) {
visible = !visible;
Cookie.set('menu', visible);
}
visible ? $('menu').show() : $('menu').hide();
}

Event.observe(window,'load',function(){
toggleMenu(false);
});
</script>


If I were to output the cookie, I would see it's being stored as:


{"menu": true}


If I wanted to add similar functionality to handle the visibility of a sidebar, my code might look like following:


<script type="text/javascript">
toggleItem = function(item, set) {
var visible = Cookie.get(item, true);
if (set) {
visible = !visible;
Cookie.set(item, visible);
}
visible ? $(item).show() : $(item).hide();
}

Event.observe(window, 'load', function(){
toggleItem('menu', false);
toggleItem('sidebar', false);
});
</script>


And my cookies would be stored as:


{"menu": true, "sidebar": false}


Not only is it storing multiple key-value pairs in a single cookie, but we have the added bonus of a really clean API for managing cookies. If you're curious, here's the full Cookie class.


<script type="text/javascript">
var Cookie = {

key: 'cookies',

set: function(key, value) {
var cookies = this.getCookies();
cookies[key] = value;
var src = Object.toJSON(cookies).toString();
this.setCookie(this.key, src);
},

get: function(key){
if (this.exists(key)) {
var cookies = this.getCookies();
return cookies[key];
}
if (arguments.length == 2) {
return arguments[1];
}
return;
},

exists: function(key){
return key in this.getCookies();
},

clear: function(key){
var cookies = this.getCookies();
delete cookies[key];
var src = Object.toJSON(cookies).toString();
this.setCookie(this.key, src);
},

getCookies: function() {
return this.hasCookie(this.key) ? this.getCookie(this.key).evalJSON() : {};
},

hasCookie: function(key) {
return this.getCookie(key) != null;
},

setCookie: function(key,value) {
var expires = new Date();
expires.setTime(expires.getTime()+1000*60*60*24*365)
document.cookie = key+'='+escape(value)+'; expires='+expires+'; path=/';
},

getCookie: function(key) {
var cookie = key+'=';
var array = document.cookie.split(';');
for (var i = 0; i < array.length; i++) {
var c = array[i];
while (c.charAt(0) == ' '){
c = c.substring(1, c.length);
}
if (c.indexOf(cookie) == 0) {
var result = c.substring(cookie.length, c.length);
return unescape(result);
};
}
return null;
}
}
</script>


Enjoy!

5 comments:

  1. great! thanks for sharing this solution, it's what I was looking for:)

    ReplyDelete
  2. Oh my god dude, this works like a charm, you rock!

    /me bows in respect

    Cheers!

    Barry

    ReplyDelete
  3. jQuery fan here, forced to use Prototype at work for legacy code. You are awesome!

    ReplyDelete