Remembered Collapsible Objects with PHP and Javascript
This is kind of a part two to my quite popular (well, popular according to the search engines) post Collapsible Tables with Javascript.
This time I went through and extended the features a little. With this script, your users won’t have to keep clicking to collapse/uncollapse objects on your pages. Through Javascript setting cookies, and PHP picking up on the cookies, your page will “remember” the state each object should be in. Thus, the things the user has chosen to collapse will stay collapsed and the things they chose to uncollapse will stay uncollapsed.
I also wanted to bring attention to newer users: almost anything can be collapsed. My last post used “table” in the title and many new users seem to think only table rows / tbody’s can be collapsed. But, of course, that is not the case. Collapsing is simply hiding, so anything that can be hidden can effectively be collapsed. This means: tables, lists, buttons, forms, divs, fieldsets etc.
Before you read, I highly suggest you download the source code to this project. It will be easier to read and understand if you can see everything at once instead of bit-by-bit.
What Needs to Happen
The first step in understanding how to create collapsible content is to understand what exactly needs to happen. It’s incredibly simple! All we need to do is toggle the value of the display property. When display is set to none, the object is totally removed from the page — exactly what we want.
To do this with Javascript is very easy, all we need to do is get a hold of the object from the page and alter the style properties directly. To do this we need to assign a unique ID to the object, then we can go ahead and tell Javascript to “get the element with the ID myid“. These days you can get objects from the page with document.getElementById('myid');, but I like to use a custom function that has a greater chance of success with older browsers. The function I use is getObj(), so when you see it throughout know that it just gets the element object from the page so we can work with it (the function is very simple and doesn’t need to be talked about — it’s just important you know what it does when you see it).
After we’ve got our element object from the page, we can fiddle around with it’s style properties. Here’s an example that will hide the element with the ID of “moreinfo”:
obj.style.display = ’none’;
And similarly, we can “show” a hidden object by simply removing the display value:
obj.style.display = ”;
Saving State
So we know how to show/hide an object, what else do we need to do? We want the state to be remembered, so we’ll have to store the state change in a cookie. Let’s make a function that will toggle the state of an object, then save that state to a cookie. First though, we have to think of values that represent the uncollapsed (content is showing) and collapsed (content is hidden) states. I think integer values of 1 and 0 work well, 1 representing uncollapsed content and 0 representing collapsed content.
* Toggle the state of an object
*
* @param string id The ID of the object to toggle
*/
function toggleCollapseState(id)
{
var obj = getObj(id);
if(!obj)
return false;
if(obj.style.display == ’none’)
{
var state = 1;
obj.style.display = ”;
}
else
{
var state = 0;
obj.style.display = ’none’;
}
saveCollapseState(id, state);
return true;
}
This is a fairly simple function. We first get the element object from the page with getObj() using the parameter id. Then, we decide what to do. If the current style.display value is 'none' (the object is currently hidden), then we want to show it. If not, then we want to hide it. Notice how we also store what we did in a variable called state, and after, we call another function called saveCollapseState. This function will do all the cookie work.
This toggleCollapseState() function will be used with the onclick event (or whatever other event you choose to use) to toggle the state of your hidden content. We’ll see it in action in a few minutes.
Once again we have to think of some kind of convention to use to store the states of objects. The convention I used looks like this: obj1=1,obj3=1,
So we have an object ID, an equal sign, and then it’s current state (remember the 1 for uncollapsed and 0 for collapsed). Each object and it’s state are separated from other objects and their states by a comma. By using this kind of convention, we can modify / delete / replace / retrieve the states very easily in Javascript and PHP. Now that we have our convention down, lets take a look at that saveCollapseState function:
* Modify the document cookie to remember the state of an object
*
* @param string id The ID of the object
* @param integer state The state of the object
*/
function saveCollapseState(id, state)
{
var cookie = readCookie(’collapseState’);
if(!cookie)
cookie = ”;
var search = new RegExp(’(' + id + ’)=([01]{1}),’);
if(search.test(cookie))
cookie = cookie.replace(search, ’$1$2=’ + state + ’,');
else
cookie += id + ’=' + state + ’,';
setCookie(’collapseState’, cookie, new Date(’January 1, 2020′));
}
There is one thing I want to say before starting to dissect this code. First is, there are two custom functions used here: readCookie() and setCookie(). As you can guess, they read cookie data and set cookie data; how to work with cookies in Javascript is beyond the scope of this post, so take a look at the source code and just know what they do when you see them.
So before we can do anything, we need to get the current data from the cookie (there’s that readCookie() function in use). Note that if the cookie was not found, the function returns null — that is the reason for the conditional right after, we want to make sure we have a string.
The next bit is defining the search regular expression object. The regular expression will make it extremely easy to search for the “entry” in our cookie for that particular object, as well as modify the state — which is exactly what we do. We test to see if the state was already stored in the cookie, and if it is, then we change the value to the new state. If the value was not found in the cookie, then we want to add it.
Finally, we re-write the cookie with the new data (setting the expiry date to some time in the far future).
The Javascript part is over! It’s not that hard to understand if you just look over it logically!
Serving the State
Now we have a cookie filled with all of this state information. It tells us what objects should be hidden and which should be shown. With PHP, we can make use of this information. First, we need to turn our big long string of data into some usable data. We do this with the getCollapseStates() function:
/**
* Get an array of the collapse states from the cookie
*/
function getCollapseStates()
{
$collapseState = array();
if(isset($_COOKIE['collapseState']))
{
$items = explode(’,', $_COOKIE['collapseState']);
unset($items[ count($items) - 1 ]);
foreach($items AS $item)
{
list($id, $state) = explode(’=', $item);
$collapseState[$id] = $state;
}
}
return $collapseState;
}
?>
This is a pretty basic function. It gets the data from the cookie, and we explode it using the comma as the separator. This will give us an array of obj=state strings (also note that we unset the last item of the array, since with our Javascript, we always have a trailing comma — if we left this last item of the array, it would be empty and give us a headache).
We then loop through these obj-state strings, and explode it again this time using the equal sign as the separator. The resulting array will have two items: the object ID and then it’s state. We feed the array to the list() function to quickly set these variables. Once we have them, we can just add them to a new, more usable array.
By the time this function is finished, we have a nicely formed array. Each array index is an object ID, and each item is that objects state (0 or 1).
The next thing we need to do is write a simple function that will output the correct style code depending on the values we got from the cookie:
/**
* Print the style according to the objects collapse state.
*
* @param string id The id of the object
* @param integer default_state If the object state was not found in the cookie,
* then what default state should it assume?
*/
function getState($id, $default_state = COLLAPSE_DEFAULT_STATE)
{
static $collapseState;
if(!isset($collapseState))
$collapseState = getCollapseStates();
if(array_key_exists($id, $collapseState))
$state = $collapseState[$id];
else
$state = $default_state;
if($state)
return ”;
else
return ’display:none;’;
}
?>
It simply gets the array from getCollapseStates(), searches for the state, and then tests to see if the object should be hidden. If it should be hidden, it returns the require display:none; style code (we’ll use this in our HTML in a minute).
You’ll also notice a second parameter: $default_state. This variable says what state the object should be by default — that is, if the state was not changed by the user (no info in the cookie), what state should it be in? It’s optional and defaults to 0 (the collapsed state, or “hidden”).
Setting up the Page
Let’s just jump right into the HTML:
<!DOCTYPE html PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN” ”http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xml:lang=”en” lang=”en” xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″ />
<title>Collapse</title>
<script type=”text/javascript” src=”global.js”></script>
<script type=”text/javascript” src=”collapse.js”></script>
</head>
<body>
<ul>
<li>
<a href=”#” onclick=”return !toggleCollapseState(’obj1′);”>Toggle Obj1</a>
<ul id=”obj1″ style=”<?php echo getState(’obj1′); ?>”>
<li>Test</li>
<li>Object 1</li>
</ul>
</li>
<li>
<a href=”#” onclick=”return !toggleCollapseState(’obj2′);”>Toggle Obj2</a>
<div id=”obj2″ style=”<?php echo getState(’obj2′); ?>”>
<h3>Object 2</h3>
</div>
</li>
<li>
<a href=”#” onclick=”return !toggleCollapseState(’obj3′);”>Toggle Obj3</a>
<input type=”button” id=”obj3″ value=”Object 3″ style=”<?php echo getState(’obj3′); ?>” />
</li>
</ul>
</body>
</html>
This page has three examples of “collapsible” content: a list, a div and a button. There are only two things of real interest. The first has to do with the links. Here is where we use that toggleCollapseState() function we discussed earlier. The first parameter is the ID of the object we want to toggle (thus, if we make a link to toggle obj3, then we ought to make sure to pass obj3 to the function).
The second thing is the PHP code inside each of the collapsible objects style. Here we call the getState() function and output whatever it gives us. If the object should be hidden, it’ll output the display:none; or visa versa. Remember, you can specify the default state as a second parameter.
Finished
There you have it! Take a look at the included zip package where all the source files are included. It’s a lot easier to understand when you get down and dirty with the code!
May 4th, 2006 at 12:45 pm
Hey…this is FANTASTIC script for a javascript newbie like me. My only question is, how do I add images (up and down arrows) to the toggle states?
July 3rd, 2006 at 8:41 am
Thanks, this script is awesome. I’ve been looking for something like this for ages now and this does exactly what I want!
August 23rd, 2006 at 2:01 pm
Very helpful code, thank you.