Rediscovering JavaScript

January 30th, 2005

It’s been a long time since I played around with JavaScript. I was pretty much under the impression it was dead and basically made editing pages more difficult. When I would go back and try and update pages that I had written long ago with a smattering of script in the header, and some strewn throughout to make things happen, I basically found that it was just too much work for the benefit it provided. With CSS I could accomplish 90% of what I used to do with JavaScript and it was much easier to read and maintain.

I think I’m a born again JavaScript fiend. After recently discovering the great site Unobtrusive Javascript, I’m once again sold. This site makes it easy to separate the script from the CSS from the HTML, and all work harmoniously together.

After playing around with their method of separating CSS and JS from the HTML I was finally able to elegantly solve an issue I’ve been wanting a solution to for years. I love how gmail does the whole stack of cards thing with your email. But more than that I love how they show and hide parts of the page arbitrarily. When you are reading a message there is a nice little reply box down at the bottom that when you put your cursor in to write a reply gets bigger, but doesn’t refresh the page so you can continue to type. And when you want to look back through some old messages all you do is click on the stack of cards and viola there you go, your old messages are right there.

My dilemma was more simplistic. I need to make a large form that I wanted to display on a single page, but not force people to scroll down through the whole thing, and I didn’t want people to have to click through sections of it because I want people to go right where they need to go without having to navigate a menu if they don’t want to. And I wanted to allow people to check over the entire form before submitting it, so they could fill in the whole thing at once if they wanted to, or could submit just parts, and through the wonders of MYSQL they could come back later and finish up.

The solution I had in mind was to show the headings for the sections of the form, and then allow people to click on the header to expand or collapse the section of the form. In addition I wanted a way to expand and collapse the whole form so that if you wanted to jump around from one section to another you could with minimal scrolling.

JavaScript to the rescue.

Using the basic function provided by the fine folks over at Unobtrusive Javascript I was able to make a page where all h2 tags turn into buttons that expand and contract their sibling divs. Then after some trial and error as I got my mind wrapped around what they were doing, I was able to take h3 tags and make them expand and contract all of the divs that immediately proceed h2 tags. The beauty is that all the CSS and all of the JavaScript are in their own files, no body tag onload() functions, no extra classes or id’s even. When CSS is needed for a specific application it is called and generated by the javascript, and the javacript file itself tells the HTML page to load it when the window loads, so all you have to change in your HTML is to include a single line telling it where to find the JavaScript file.

Alright, enough talk here are the three files: (note this is part of a php file so the values for the form fields are self populating with php code, get rid of it if you don’t want this.)

First the HTML File:

<html>
<head>
</head>
<body>
<h3>Collapse All Sections</h3>
<form action="processupdates.php" method="post">
<h2>Personal Inforamtion</h2>
<div>
<label for="FirstName">First Name: </label>
<input type="text" id="FirstName" name="FirstName" value="First" size="30" maxlength="60" tabindex="1"/>
<label for="LastName">Last Name: </label>
<input type="text" id="LastName" name="LastName" value="last" size="30" maxlength="60" tabindex="2"/>
</div>
<h2>Other Inforamtion</h2>
<div>
<label for="FirstName">First Name: </label>
<input type="text" id="FirstName" name="FirstName" value="<? echo $line["FirstName"]; ?/>" size="30" maxlength="60" tabindex="1">
<label for="LastName">Last Name: </label>
<input type="text" id="LastName" name="LastName" value="<? echo $line["LastName"]; ?/>" size="30" maxlength="60" tabindex="2">
</div>
<h2>More Inforamtion</h2>
<div>
<label for="FirstName">First Name: </label>
<input type="text" id="FirstName" name="FirstName" value="<? echo $line["FirstName"]; ?/>" size="30" maxlength="60" tabindex="1">
<label for="LastName">Last Name: </label>
<input type="text" id="LastName" name="LastName" value="<? echo $line["LastName"]; ?/>" size="30" maxlength="60" tabindex="2">
</div>
<br />
<input type="submit" id="Save Application" value="submit"/>
</form>
<h3>Collapse All Sections</h3>
</body>
</html>

Next the CSS:

body{
background:#f8f8f8;
color:#333;
font-family:Arial, Helvetica, sans-serif;
}
h2{
font-size:110%;
font-weight:normal;
width: 40em;
}
h3{
font-size:110%;
font-weight:normal;
width: 40em;
}
.hidden{
display:none;
}
.shown{
display:block;
}
.trigger{
background:#ccf;
cursor:n-resize;
}
.open{
background:#66f;
cursor:s-resize;
}
.hover{
background:#99c;
}

Finally the JavaScript:

function collapse()
{
//Collapses and expands siblings of h2 tags in html file
if(!document.createTextNode){return;}
var heads=document.getElementsByTagName('h2');
for(var i=0;i<heads .length;i++)
{
//Figure out what should be opened and closed.
var tohide=heads[i].nextSibling;
while(tohide.nodeType!=1)
{
tohide=tohide.nextSibling;
}
//open all parts that should be open, and set their parents to be ready to close them
cssjs('add',tohide,'shown')
cssjs('add',heads[i],'trigger')
heads[i].tohide=tohide;
//Make them act more like links so the user gets a response
heads[i].onmouseover=function()
{
cssjs('add',this,'hover');
}
heads[i].onmouseout=function()
{
cssjs('remove',this,'hover');
}
heads[i].onclick=function()
{
if(cssjs('check',this.tohide,'hidden'))
{
cssjs('swap',this,'trigger','open');
cssjs('swap',this.tohide,'hidden','shown');
} else {
cssjs('swap',this,'open','trigger');
cssjs('swap',this.tohide,'shown','hidden');
}
}
}
var mainhead=document.getElementsByTagName('h3');
for(var i=0;i<mainhead.length;i++)
{
//run through and make h3's into close and open all links
cssjs('add',mainhead[i],'trigger');
this.innerHTML = 'Collapse all Sections';
mainhead[i].onmouseover=function()
{
cssjs('add',this,'hover');
}
mainhead[i].onmouseout=function()
{
cssjs('remove',this,'hover');
}
mainhead[i].onclick=function()
{
heads=document.getElementsByTagName('h2');
if(cssjs('check',this,'trigger'))
//This runs through and opens all sections cleaning up the h2's so that they are ready to close again
{
for(var j=0;j<heads.length;j++)
{
if(cssjs('check',heads[j],'trigger'))
{
cssjs('swap',heads[j],'trigger','open');
cssjs('swap',heads[j].tohide,'hidden','shown');
}
}
for(var l=0;l<mainhead.length;l++)
{
//Makes sure that all h3s are ready to close the sections
mainhead[l].innerHTML = 'Expand all Sections';
cssjs('swap',mainhead[l],'trigger','open');
}
} else {
//This runs through and closes all sections cleaning up the h2's so that they are ready to open again
for(var k=0;k<heads.length;k++)
{
if(cssjs('check',heads[k],'open'))
{
cssjs('swap',heads[k],'open','trigger');
cssjs('swap',heads[k].tohide,'shown','hidden');
}
}
for(var m=0;m<mainhead.length;m++)
{
//Makes sure that all h3s are ready to re-open the sections
cssjs('swap',mainhead[m],'open','trigger');
mainhead[m].innerHTML = 'Collapse all Sections';
}
}
}
}
}
function cssjs(a,o,c1,c2)
{
//Applies action a to object o by adding, removing, swapping, or checking for classes c1 or c2.
switch (a){
case 'swap':
o.className=!cssjs('check',o,c1)?o.className.replace(c2,c1):o.className.replace(c1,c2);
break;
case 'add':
if(!cssjs('check',o,c1)){o.className+=o.className?' '+c1:c1;}
break;
case 'remove':
var rep=o.className.match(' '+c1)?' '+c1:c1;
o.className=o.className.replace(rep,'');
break;
case 'check':
return new RegExp('b'+c1+'b').test(o.className)
break;
}
}
//make it all happen when the page loads
window.onload=collapse;

New Styles

March 24th, 2004

I’ve made a couple of new styles for the site, nothing spectacular but I do like the blue motif. Links for the styles are now listed at the bottom of the menu. I’m thinking I might have to go the way of the Zen Garden and add in some extra html hooks if I want to explore the possibilities of css. I also looked into refer 2.1 (see below) and have come to the conclusion that I can’t really use it since I don’t have a MySQL database on this site. So sad. If anyone has a different way of managing referrers (posting them automatically) I’d be interested in hearing about it.
I’m also looking for a good blogger that allows replies, but has great filtering too since I’m not really in the market to enlarge my member.

Thanks Ethan for the fix

March 24th, 2004

Thanks to Ethan over at sidesh0w.com for solving my little “CSS” bug, actually pilot error, using padding to keep the main text flow from overlapping my menu instead of margins. It seems I effectively placed my menus under a nice glass ceiling that they just couldn’t get through. Now we can all enjoy the intended look.
I think I should also pay tribute to Dean Allen and put up a copy of his Refer 2.1 as that was how Ethan found me. I am truly amazed each day at the community that exists in cyberspace. Thanks Ethan. Now maybe I should invest some time finding a good blogger as it seems I might have something to say.