Javascript-A-Palooza!
So, I started off with a simple, five-page, static website. Piece of cake. I design it - it was rough going there for a bit as I tried to find the look for this site - but in the end I’m really stoked on it. I go to implement it in xhtms/css and all goes relatively smoothly. Yay.
But then … on the home page … I’m loving how it looks with minimal content but there are a few more paragraphs that I’d like to be able to add. I make a “read more” link at the bottom of the minimal content but I’m thinking that I dont’ really want to go to a new page when I click “read more,” because the “more” text doesn’t really deserve it’s own page. I think, “Hey it would be cool if when I click ‘read more’ then content box just expands and then there’s the “more”. I know how to do that!”
Ah, such simple thinking. Little did I know this would lead me down an obsessive path and a steep javascript learning curve to produce several pages with chunks of content which can be turned off and on by clicking links while at the same time keeping everything semantic and good to go for those with javascript disabled.
Disclaimers
Three things: First, the site referred to in this article (MichaelRehl.com) is not finished - I’m still working on it, but it is finished enough for the purposes of this article.
Secondly, I am going to try my darndest not to make this too long winded (who, me be long winded?!) but there is a lot of ground to cover here. For the sake of brevity I will not detail the many solutions I came up with before the one I am going to share with you. These solutions worked, but they were either: not semantic, not accessible to those with javascrpt turned off, or had exceedingly long “flashes” of content while javascript hid things after they had already loaded. I also will not detail all my code snippets as I usually do (just select ones) - but feel free to peek under the hood yourselves any time. ;o)
Finally, I owe a LOT of thanks to my friend, David Hucklesby, who fielded my despearately frustrated phone calls, pointed me to an invaluable resource, wrote a little script for me, and tested my efforts on a dial-up connection - thanks, David!
The Home Page
Here’s what I wanted …
With Javascript Turned On: I wanted a nice, neat, fit-in-the-whole-screen page with just a little bit of writing and then a “read more” link. When you click the link the content box expands and there’s the “more” text, with another link at the bottom that says “close” which closes the additional content when clicked.
With Javascript Turned Off: I wanted all the content visible, with no “read more” or “close” links visible.
The Challenge
I needed the content to be visible by default, and the links to be invisible by default. Then I needed to reverse this scenario using Javascript, but I couldn’t use <body onload=”runMyScript()> because I needed the javascript to do its magic before the page loaded to avoid the yucky flash of content.
The Solution: Dynamic CSS
So David sends me a link to an article by Bobby van der Sluis entitled Using dynamic CSS to hide content before page load. Sounds perfect, eh? Well it looked too complicated for me so I left it alone until I had exhausted all other efforts. ;o)
Anyway, I grabbed a copy of his script and linked to it from the head of my document. Now directly below the link I put this:
<script type="text/javascript">
if (document.getElementById) {
createStyleRule("#index_more", "display:none;");
createStyleRule("#read_more", "display:block;");
createStyleRule(".close", "display:block;");
}
</script>
This does just what I want - it reverses the default display of the links and content before the page loads. Now I just have to add some javascript calls on my links to open and close the content like so:
<p id="read_more">» Read more</p>
<div id="index_more">
--ADDITIONAL CONTENT --
<p class="close">»Close</p>
</div>
» See it in action with Javascript and without Javascript.
The Services and FAQ Pages
For the purposes of this article I will refer only to the Services page in my code examples, but the same principals apply to both pages.
I wanted to apply the same idea to the Services and FAQ pages as I had to the home page because I, personally, am often overwhelmed by a page with many jumplinks at the top and tons of scrolling text underneath peppered with “back to top” links. I am not likely to look through a page like that unless I really need the content. So I wanted to create brief, easy to read, un-overwhelming screens with minimal content on each, but I did not want to create many separate pages or have to reload the page each time I want to see a different service or different faq.
So here’s what I want …
With Javascript Turned On: This time it was all the content (the answers to the frequently asked questions or the description of the services) that I wanted to be invisible by default as well as the “top” links) and the intro paragraph (the one instructing the user to click the links to the right) that needed to be visible by default. But also I wanted the links on the right to reveal their corresponding content (but without “jumping” to the heading of that content”) and hide any content that is currently visible.
With Javascript Turned Off: Of course I wanted the reverse of above in terms of what is visible and invisible by default. Additionally, when I click on links on the right I wanted to jump down to the heading of the corresponding content, as in traditional jumplinks.
Dynamic CSS to the Rescue Again
So I link to the same DynamicCSS.js file in the head and directly below the links I put this:
<script type="text/javascript">
if (document.getElementById) {
createStyleRule("#intro", "display:block;");
createStyleRule("#a1", "display:none;");
createStyleRule("#a2", "display:none;");
createStyleRule("#a3", "display:none;");
createStyleRule("#a4", "display:none;");
createStyleRule("#a5", "display:none;");
createStyleRule("#a6", "display:none;");
createStyleRule("#a7", "display:none;");
createStyleRule("#a8", "display:none;");
createStyleRule(".top", "display:none;");
}
</script>
I also link to the following additional (new) script:
<script type="text/javascript" src="../_includes/showHide.js"></script>
This script holds functions that I will call to from within the links on the right and looks something like this:
function serv1() {
createStyleRule("#intro", "display:none;");
createStyleRule("#a1", "display:block;");
createStyleRule("#a2", "display:none;");
createStyleRule("#a3", "display:none;");
createStyleRule("#a4", "display:none;");
createStyleRule("#a5", "display:none;");
createStyleRule("#a6", "display:none;");
createStyleRule("#a7", "display:none;");
createStyleRule("#a8", "display:none;");
document.getElementById('q1').className='on';
document.getElementById('q2').className='off';
document.getElementById('q3').className='off';
document.getElementById('q4').className='off';
document.getElementById('q5').className='off';
document.getElementById('q6').className='off';
document.getElementById('q7').className='off';
document.getElementById('q8').className='off';
return false;
}
function serv2() {
createStyleRule("#intro", "display:none;");
createStyleRule("#a1", "display:none;");
createStyleRule("#a2", "display:block;");
createStyleRule("#a3", "display:none;");
createStyleRule("#a4", "display:none;");
createStyleRule("#a5", "display:none;");
createStyleRule("#a6", "display:none;");
createStyleRule("#a7", "display:none;");
createStyleRule("#a8", "display:none;");
document.getElementById('q1').className='off';
document.getElementById('q2').className='on';
document.getElementById('q3').className='off';
document.getElementById('q4').className='off';
document.getElementById('q5').className='off';
document.getElementById('q6').className='off';
document.getElementById('q7').className='off';
document.getElementById('q8').className='off';
return false;
}
function serv3() {
...
And in my xhtml page I do this:
<div id="sidecol">
<ul>
<li id="q1">deep tissue work</li>
<li id="q2">massage</li>
<li id="q3">joint mobilization</li>
<li id="q4">neuromuscular reeducation</li>
<li id="q5">muscle testing</li>
<li id="q6">nutritional counseling</li>
<li id="q7">supplements</li>
<li id="q8" class="last">pilates</li>
</ul>
</div>
What I’m doing is saying: When I click on “massage” (the 2nd link on the right) then return the function “serv2″ which hides all pieces of content except for the desired one. It also gives a class of “off” to all the links on the right except for the one which is being clicked, to which it gives a class of “on.” This simply gives the clicked link a background of white, as defined in the stylesheet.
What’s The “Return” For?
At one point I had this all working except that when Javascript was enabled and I clicked on one of the links on the right, the appropriate content was revealed but the page jumped down to the heading of that content. This, I learned, is because it was still following the default behavior of the link, and in order to prevent it from doing that I had to add “return” before my function name, and then at the end of my function I had to insert a line that stated, “return false;” - and this killed the default link behaviour. =)
» See it in action with Javascript and without Javascript.
But What About the Links From the Home Page?
Oh yeah … just when I thought I was done - I tried clicking on the links to the individual services from the home page and they just took me to the default services display, i.e. - all I see is the intro sentence telling me to click the links on the right. That’s not what I want! I want to click “massage” and come to the Services page with only the “massage” content showing, and with the “massage” link lit up. Hmmm …
Well, afer much unproductive effort I turn to my javascript guru, David and he writes a sweet little script for me that I place in my ShowHide.js document:
function showRequestedService() {
var url = window.location.href;
var start = url.lastIndexOf('#') + 1;
var fragmentId = ''; // empty string
if (start > 0 && url.length > start)
fragmentId = url.substring(start, url.length);
switch (fragmentId) {
case 'a1':
serv1();
break;
case 'a2':
serv2();
break;
case 'a3':
serv3();
break;
case 'a4':
serv4();
break;
case 'a5':
serv5();
break;
case 'a6':
serv6();
break;
case 'a7':
serv7();
break;
case 'a8':
serv8();
break;
}
}
Now I’m not going to try to pretend I understand exactly how it does it, but this function evaluates the incoming URL and serves up the correct script in response. Sweet. Now all we have to do is call the function from the body:
<body onload="showRequestedService()">
That’s a Wrap!
Wow. My head hurts. Hope you can make heads or tails of all this! If you’re having a hard time with it take a look at the xhtml source code and the scripts, which are here:
If all else fails give me a shout and I’ll see if I can help you out. Cheers!
This tutorial has been updated! Please see Javascript-A-Palooza Update for important updates that allow this to work with IE.

January 26th, 2006 at 4:58 pm
I’m just quickly skimming this article (will read in depth tomorrow). How do you achieve the Read More action (if it’s not listed above).
January 26th, 2006 at 5:55 pm
Sian - The read more link looks like this:
It uses the dynamicCSS.js file to set the display of “index_more” to block, thus revealing it (remember it was set to display:none in the Javascript in the head.
January 28th, 2006 at 12:36 pm
*tap dances*. Thanks to your tutorial above I’ve just worked out how to achieve something that I’ve been trying to do for months! Yay!
On my webpage I have a very long list of archive pages which I wanted to disappear until a viewer needed them. I inserted the dynamiCSS.js script added the code quoted above and hey presto!
March 22nd, 2006 at 12:53 pm
How would you go about implementing something similar to the “Read More” script using more than one on a page?
What I mean is, imagine you have multiple news items on a page — lets say four — if you have the first half of a paragraph of each and wish to have the “Read More” expansion link to avoid clutter, it only works for the first read more link, no matter how many you put on a page. If you click the fourth item’s “Read more” button, it will only expand the first one?
The solution to such an issue would require a mix of code between the basic “Read more” script with a touch of your “FAQ” section solution, without all of the content hiding, etc.
Care to assist with this sort of problem? I have been spending quite some time trying to find the best solution.
Cheers,
D
March 22nd, 2006 at 1:30 pm
Scratch that. I managed to hack up the dynamicCSS.js script by calling new element ID’s in the header to manage it that way.
Now I am just hunting down a way to prevent the “read more” link popping back up to the top of the page.
Thanks again for the wonderful tutorial!
Cheers,
D
March 22nd, 2006 at 1:39 pm
Very nice. Last reply, I promise :)
Okay, sorted that all out and it really helped me out with the project I was working on. I would really like to thank you once again for sharing this with all of us.
The great thing about working with the DOM is the elegance and ability to make it gracefully semantic, the bad thing is that finding any useful information amidst all of the terrible DHTML fluff out there is slim pickings.
Leep up the great work,
D.
March 23rd, 2006 at 1:31 pm
I believe that if you wanted to do multiple “read mores” on one page, you could just give each of the “expanded view” divs a unique ID.
Then you could create functions, like the services functions, that hide and display each of those expanded sections, you follow?
Let me know if you need a more detailed response and I can get on it after the honeymoon. ;o)