reptile7's JavaScript blog
Saturday, December 22, 2007
 
Tag Free
Blog Entry #98

In this post, we'll examine HTML Goodies' JavaScript Script Tips #79 and #80 and their script that removes the markup tags, and thus extracts the #PCDATA text, of a document or document fragment. The Script Tips #79-80 Script was originally designed to work with HTML document code but should also be applicable to code written in XML or in any other markup language employing SGML-type element start-tags and end-tags.

In the course of working through it recently, the Script Tips #79-80 Script initially rubbed me the wrong way philosophically; I thought, "Man, the scripts we've discussed up to this point would generally be improved by more structural markup, not less." But it subsequently occurred to me that the script could be useful for removing the presentational markup that clogs many of these same scripts like artery plaques. Joe for his part states at the beginning of Script Tip #79 that he has used [the script] to death, although he doesn't say specifically what he's used it for.

The Script Tips #79-80 Script can be accessed by following the Here's the Code links in both script tips and is reproduced in the div below:

<html>
<head>
<title>The Script Tips #79-80 Script Demo Page</title>

<script language="javascript" type="text/javascript">

function DelHTML(HTMLWord)
{
a = HTMLWord.indexOf("<");
b = HTMLWord.indexOf(">");

HTMLlen = HTMLWord.length;

c = HTMLWord.substring(0, a);

if (b == -1)
b = a;

d = HTMLWord.substring((b + 1), HTMLlen);

Word = c + d;

tmp = Word.indexOf("<");

if (tmp != -1)
Word = DelHTML(Word);

return Word;
}

function doit( )
{
ToCheck = window.document.forms["Check"].elements["Input"].value;

Checked = DelHTML(ToCheck);

window.document.forms["Check"].elements["Output"].value = Checked;
}
</script>

<body bgcolor="#FFFFFF">

<form name="Check">
<textarea cols="50" name="Input" rows="6">
</textarea>
<input onclick="doit( );" type="button" value="Remove all HTML Tags" />
<textarea cols="50" name="Output" rows="6">
</textarea> 
</form>

</body>

</html>

The Check form

The Script Tips #79-80 Script's display is housed in a form named Check. As shown at the script's demo page, the Check form comprises three controls, in source order:
(1) a textarea box, named Input, into which the user inputs a document or document fragment;
(2) a Remove all HTML Tags button, which when clicked sends the user's input to the doit( ) function in the script's script element for tag removal; and
(3) a textarea box, named Output, to which doit( ) writes the tag-removed document or document fragment.

Validation notes
• The Check form does not have an action attribute, which has a #REQUIRED designation even in the HTML 4.01 Transitional DTD.
XHTML 1.0 deprecates the name attribute of the form element.
For validating the script document against the XHTML 1.0 Strict DTD, then, recast the form element start-tag as <form action="">, and use document.forms[0] to reference the form in the doit( ) function, if you'd like to keep the form element container (modern browsers don't need it but at least some older browsers won't render controls outside of a parent form).
• In the original script, all but one of the attributes of the Check form and its controls are unquoted; quote them for XHTML-compliance.

One more point before moving on. In Script Tip #79, Joe says, Notice the order in which [the Check controls] are written to the page. Keep them in that order: box, button, box. It helps the script to run correctly. Actually, the associative formObject.elements["controlName"] references for the Input and Output boxes in the doit( ) function are unrelated to the positions of these controls within the Check form; consequently, an alternate control order - box, box, button, or perhaps button, box, box - wouldn't cause any problems vis-à-vis the script's execution. (However, had the script's author, "CompuH@cker", used ordinal formObject.elements[0] and formObject.elements[2] references for the Input and Output boxes, respectively, then Joe would be correct.)

Deconstructing the Script Tips #79-80 Script

We're ready to deconstruct the JavaScript part of the Script Tips #79-80 Script, which is for the most part quite straightforward. As a test case, let's suppose that we enter the string

<span style="color:brown;">Brown text</span> looks really cool on a Web page

into the Input box and then click the Remove all HTML Tags button, triggering the doit( ) function. (Contra Script Tip #80, the doit( ) function is not actually sitting inside of a larger function named DelHTML( ), which we'll get to shortly; rather, doit( ) is external to DelHTML( ).)

function doit( ) {
ToCheck = window.document.forms["Check"].elements["Input"].value;

Our test string is assigned to the variable ToCheck.

Checked = DelHTML(ToCheck);

This line calls the DelHTML( ) function, which precedes doit( ) in the script element, and passes ToCheck thereto. Later, DelHTML( )'s output will be assigned to the variable Checked.

function DelHTML(HTMLWord) {

ToCheck, the input string, is given a new identifier, HTMLWord.

a = HTMLWord.indexOf("<");
b = HTMLWord.indexOf(">");

The index of the first < character in the HTMLWord string, 0, is assigned to the variable a; similarly, the index of the first HTMLWord > character, 26, is assigned to the variable b.

HTMLlen = HTMLWord.length;

The HTMLWord length value, 77, is assigned to the variable HTMLlen.

c = HTMLWord.substring(0, a);

HTMLWord.substring(0, 0) returns an empty string, which is assigned to the variable c.

if (b == -1) b = a;

In Script Tip #80, Joe poses (but does not satisfactorily answer) a What if there's no >? question, to which the above if statement would pertain. We'll address this situation, and Joe's follow-up What if there are no tags? question, in the "> without <, and vice versa" section below. For now, the if condition returns false and the browser moves on to...

d = HTMLWord.substring((b + 1), HTMLlen);

HTMLWord.substring(27, 77) returns the string
Brown text</span> looks really cool on a Web page,
which is assigned to the variable d.

Word = c + d;

c and d are concatenated to give the string
Brown text</span> looks really cool on a Web page,
which is assigned to the variable Word.

tmp = Word.indexOf("<");

The index of the first < character in the Word string, 10, is assigned to the variable tmp.

if (tmp != -1)
[Word = ]DelHTML(Word); /* The part of this line in square brackets is unnecessary. */

The if condition above returns true, so DelHTML( ) is re-called and the Word string is passed thereto.

It is left to the reader to verify that on our second run through the DelHTML( ) function:
(1) the Word string is given the HTMLWord identifier;
(2) the a index will be 10;
(3) the b index will be 16;
(4) the HTMLlen length will be 50;
(5) the c substring will be Brown text;
(6) the d substring will be  looks really cool on a Web page (d will begin with a space character);
(7) the Word string will be Brown text looks really cool on a Web page;
(8) tmp will be -1; the tmp != -1 condition is now false, so the browser moves to...

return Word;

The now-detagged Word string is returned to the doit( ) function and given the identifier Checked, as noted earlier.

window.document.forms["Check"].elements["Output"].value = Checked;

Finally, the Checked string is loaded into the Output box.

> without <, and vice versa

So, what if there's no >, huh? Let's suppose that our input string is
Experts agree that 4<5 most of the time;
what happens? A trial run at the script demo page shows that the script merely subtracts the < character and outputs
Experts agree that 45 most of the time.
Complementarily, let's suppose that our input string is
Studies show that 4>5 for large values of 4;
this time, the script chops off the > character plus the substring that precedes it, and outputs
5 for large values of 4.
Probably not what you would want, is it?

However, if we input a tagless string lacking </> characters, for example,
It's time for another cup of the hot, black liquid,
then the script does at least return
It's time for another cup of the hot, black liquid
without incident.

Of course, all inputted strings without tags should be outputted unchanged; this is easily achievable via the following minor script modifications:

(1) Replace the DelHTML( ) function's if (b == -1) b = a; conditional with

if (a == -1 || b == -1) return HTMLWord;

(2) Wrap DelHTML( )'s subsequent statements in an else { ... } container.

Speaking of unbalanced </> situations:

• It should be emphasized that the script won't work properly if the inputted code's tags contain any errors with respect to their delimiting < and > characters; e.g.,

<a href="http://www.someWebSite.com/" This is a link.</a>

will not return its link text.

• You may be wondering, "What if the inputted code itself has a script element containing one or more < and/or > characters?" In this case, you should externalize the script element code before applying the Script Tips #79-80 Script to the remaining code.

A non-iterative 'detagification'

My simple deconstruction example contained only two HTML tags. But what if we were to input an entire document with dozens or even hundreds of tags? For the DelHTML( ) function to run through such a document over and over and over again - oh, my aching CPU! - is rather inefficient, needless to say. Isn't there some way to pick off all those tags in one sweep? It turns out that we can replace the DelHTML( ) function with two lines of regular expression-based code that will allow us to do just that; recast the doit( ) function as:

var ToCheck, SGMLTag, Checked;
function doit( ) {
ToCheck = document.forms[0].Input.value;
SGMLTag = /<[^>]+>/g;
Checked = ToCheck.replace(SGMLTag, "");
document.forms[0].Output.value = Checked; }

The SGMLTag <[^>]+> regexp pattern matches any valid tag:
• The starting < and ending > tag characters appear literally; they are not regexp metacharacters and do not need to be escaped with backslashes.
• The [^>] negated character matches one character that is not a >.
• The [^>] character is matched one or more times via the + quantifier.
The accompanying g flag 'globalizes' the matching process, i.e., all SGMLTag-tag matches in the document are returned (without the g, the browser would only return the first SGMLTag-tag match).

The replace( ) method of the String object is discussed by Mozilla here. The above replace( ) command replaces each SGMLTag in the inputted ToCheck string with an empty string, in effect subtracting the document's tags.

I've taken the <[^>]+> regexp pattern from this section of Jan Goyvaerts' Regular-Expressions.info Web site, a regular expressions resource that I highly recommend.

In the following entry, we'll check over the Script Tips #81-83 Script, which creates various multicolor text strings.

reptile7

Wednesday, December 12, 2007
 
Drop-Down ULs
Blog Entry #97

Believe it or not, we have never, not once, worked with HTML lists at this blog - we'll do that today! As noted at the end of our last episode, this post will reformulate HTML Goodies' "Drop-Down Menu" tutorial script with a nested list structure. Let me kick things off by giving you some relevant references:
(1) HTML Goodies covers HTML lists in its "So You Want Indents and Lists, Huh?" tutorial.
(2) In the HTML 4.01 Specification, HTML lists are detailed in Chapter 10 ("Lists").
(3) On the style front, HTML lists are addressed in Section 12.5 of the CSS 2.1 Specification.

The list HTML

Here's the list HTML we'll use for the "Drop-Down Menu" tutorial script:

<ul id="outerlist">
<li id="heading1" onclick="displayList(1);">Goodies Tutorials</li>
<ul id="innerlist1">
<li><a href="http://www.htmlgoodies.com/tutorials/frames/">Frames</a></li>
<li><a href="http://www.htmlgoodies.com/tutorials/tables/">Tables</a></li>
<li><a href="http://www.htmlgoodies.com/beyond/dhtml/">DHTML/Layers</a></li>
<li><a href="http://www.htmlgoodies.com/tutorials/forms/">Forms</a></li>
</ul>
<li id="heading2" onclick="displayList(2);">Great Sites</li>
<ul id="innerlist2">
<li><a href="http://www.straightdope.com">The Straight Dope</a></li>
<li><a href="http://www.gutenberg.org/wiki/Main_Page">Project Gutenberg</a></li>
<li><a href="http://www.howstuffworks.com/">HowStuffWorks</a></li>
<li><a href="http://www.snopes.com/">Snopes.com</a></li>
</ul>
<li id="heading3" onclick="displayList(3);">News Sites</li>
<ul id="innerlist3">
<li><a href="http://www.cnn.com">CNN</a></li>
<li><a href="http://www.nytimes.com/">New York Times</a></li>
<li><a href="http://www.bbc.co.uk/">BBC</a></li>
<li><a href="http://www.afp.com/english/home/">Agence France-Presse</a></li>
</ul></ul>

The above code has been adapted from the code appearing in the "Can I put these together?" section of the aforecited HTML Goodies tutorial. A parent id="outerlist" ul element has as children
(a) three id="heading#" li elements that respectively hold the Goodies Tutorials, Great Sites, and News Sites menu headings; and
(b) three id="innerlist#" ul elements whose li children hold the menu links (these are the same links that I used for my demo in the previous entry).

A couple of points regarding Joe's tutorial code:
• The li elements lack closing </li> tags; this is allowed in HTML but you'll need to add these tags if you're shooting for XHTML-compliance.
• The ul list items are prefaced not with disc/square/circle markers (the ul type attribute for setting these values has been deprecated) but with blue triangular Joe's blue triangular li marker images; this can't be done with HTML but can with CSS, bringing us to...

The style sheet

#outerlist {
list-style-image: url(littlex.gif);
list-style-position: inside; }
/* This rule set can be shorthanded to:
#outerlist { list-style: inside url(littlex.gif); } */

#innerlist1, #innerlist2, #innerlist3 {
list-style-image: url(littledash.gif);
margin-top: 0; margin-bottom: 0; }

#heading1, #heading2, #heading3 { font-weight: bold; }

/*
a:link { color: black; text-decoration: none; }
a:hover { color: blue; text-decoration: underline; }
The a:link rule set runs counter to the traditional rendering of links by most browsers, whereas the a:hover rule set is redundant vis-à-vis that rendering; my preference is to comment them out.
*/

The list-style-image property sets the image that will be used as the list item marker; the URL value in parentheses can be quoted or not quoted. The The small plus image littlex.gif image that will mark the outerlist menu headings is available here and the The small minus image littledash.gif image that will mark the innerlist# menu links is available here.

(BTW, Joe uses a
ul { list-style-image:url("../img/bullet-2005.gif"); }
rule in a style sheet here to set his Joe's blue triangular li marker ul li markers; the bullet-2005.gif image itself is available here.)

The list-style-position:inside; declaration that is applied to the outerlist list (actually, it's also applied to the innerlist# lists, because list-style-position is an inherited property) places the littlex.gif markers in the same CSS "principal block boxes" that hold the heading# li elements; consequently, the onclick="displayList(#);" triggers for the displayList( ) function (vide infra), which drops down and pulls up the menu links, will apply to both the heading# headings and the littlex.gif markers that precede them. In other words, without this declaration the user would not be able to display the links by clicking on the outerlist list markers but could still do so by clicking on the outerlist list items.

Regarding vertical alignment within the li element boxes, the image markers are given a top positioning by MSIE 5.1.6 on my computer, whereas Netscape 7.02 gives them a baseline positioning; I would prefer that they have a middle positioning, but the W3C warns, CSS 2.1 does not specify the precise location of the marker box, and several attempts on my part to shift vertically the marker location came to naught.

Finally, the margin-top and margin-bottom properties for the innerlist# ul elements have been set to 0. Without these declarations, MSIE inserts 'blank lines' between the menu headings and sets of links

An image showing the default innerlist# margins when using MSIE 5.1.6

but Netscape, whose html.css file already contains a
ul ul { margin-top:0;margin-bottom:0; }
rule set, does not. (A tip of the hat to Eric Meyer and his interesting "Really Undoing html.css" essay for helping me sort out this issue.)

The script element

<script type="text/javascript">

document.getElementById("innerlist1").style.display = "none";
document.getElementById("innerlist2").style.display = "none";
document.getElementById("innerlist3").style.display = "none";

function displayList(num) {
if (document.getElementById("innerlist" + num).style.display == "none") {
document.getElementById("innerlist" + num).style.display = "block";
document.getElementById("heading" + num).style.listStyle = "inside url(littledash.gif)"; }
else {
document.getElementById("innerlist" + num).style.display = "none";
document.getElementById("heading" + num).style.listStyle = "inside url(littlex.gif)";
} }
</script>

Like the previous post's displayx( ) function, on which it is patterned, the displayList( ) function handles all three innerlist# menus. Note that the list-style property can be written at the li element level as well as at the ul/ol element level (this is also true for the list properties shorthanded by list-style).

Strangely, if I delimit the littledash.gif/littlex.gif URL values with single quotes, e.g.,

document.getElementById("heading" + num).style.listStyle = "inside url('littledash.gif')";

then MSIE replaces the original heading# image markers with disc markers (and gives them a baseline vertical alignment - go figure), as though the list-style images were not available - disc is the initial value of the list-style-type property, which specifies [the] appearance of the list item marker if 'list-style-image' has the value 'none' or if the image pointed to by the URI cannot be displayed; this problem does not arise if the list-style URLs are unquoted or delimited with \" sequences:

document.getElementById("heading" + num).style.listStyle = "inside url(\"littledash.gif\")";

With all three URL quote formats, Netscape toggles the heading# image markers without incident.

If you'd like the links to open in new browser windows, then you could somewhat tediously add target="_blank" attributes to all of the anchor element start-tags in the list HTML, but you can also accomplish this via a single line of JavaScript:

for (i = 0; i < document.links.length; i++) { document.links[i].target = "_blank"; }
/* Validation note: for XHTML-compliance, externalize the script element code if the above line is included. */

Demo

Try it all out in the div below; per the discussion/code above, you can drop down and pull up the Goodies Tutorials, Great Sites, and News Sites menu links by clicking on the heading The small plus image/The small minus image image markers or on the headings themselves:


We'll return to HTML Goodies' JavaScript Script Tips in the next post and take up the Script Tips #79-80 Script, which subtracts markup tags from a document or document fragment.

reptile7

Sunday, December 02, 2007
 
Drop-Down Menus, Act III
Blog Entry #96

HTML Goodies' DHTML/Layers sector contains a "Drop-Down Menu" tutorial that predates slightly and prefigures to an extent the rolling menu script of Script Tips #76-78 we've been analyzing in the last couple of entries. The "Drop-Down Menu" tutorial offers a script whose effect mimics the nested drop-down menus that once appeared (but no longer appear) at Microsoft's home page but are still used extensively at Microsoft's MSDN Library; this script, which in its original form only works with MSIE 4+, can be accessed here and is reproduced in the div below:

<html>
<head>

<title>Pull-Down Menus</title>

<style type="text/css">

#menu1 { display: none; }
#menu2 { display: none; }
#menu3 { display: none; }

a:link { color: black; text-decoration: none; }
a:hover { color: blue; text-decoration: underline; }

</style>

</head>

<body bgcolor="white">

<table border="0" align="left">

<tr>
<td valign="top" width="200">
<span onmouseover="document.all.menu1.style.display = 'block';" onmouseout="document.all.menu1.style.display = 'block';">
<font size="-1"><b><img src="littlex.gif">&nbsp;Goodies Tutorials</b></font></span><br>
<span id="menu1" onclick="document.all.menu1.style.display = 'none';">

<font size="-1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="littledash.gif">&nbsp;<a href="/tutors/frame1.html">Frames</a></font><br>
<font size="-1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="littledash.gif">&nbsp;<a href="/tutors/tbl.html">Tables</a></font><br>
<font size="-1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="littledash.gif">&nbsp;<a href="/beyond/dhtml.html">DHTML</a></font><br>
<font size="-1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="littledash.gif">&nbsp;<a href="/tutors/forms.html">Forms</a></font>

</span>
</td>
</tr>

<tr>
<td valign="top" width="200">
<span onmouseover="document.all.menu2.style.display = 'block';" onmouseout="document.all.menu2.style.display = 'block';">

<font size="-1"><b><img src="littlex.gif">&nbsp;Great Sites</b></font></span><br>

<span id="menu2" onclick="document.all.menu2.style.display = 'none';"> 

<font size="-1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="littledash.gif">&nbsp;<a href="http://www.straightdope.com">The Straight Dope</a></font><br>
<font size="-1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="littledash.gif">&nbsp;<a href="http://www.vhnd.com">Van Halen News Desk</a></font><br>
<font size="-1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="littledash.gif">&nbsp;<a href="http://www.dave-tv.com">Dave TV</a></font><br>
<font size="-1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="littledash.gif">&nbsp;<a href="http://www.hairclub.com">Hair Club for Men</a></font>

</span>
</tr>

<tr>
<td valign="top" width="200">
<span onmouseover="document.all.menu3.style.display = 'block';" onmouseout="document.all.menu3.style.display = 'block';">

<b><img src="littlex.gif">&nbsp;<font size="-1">News Sites</font></b></span><br>

<span id="menu3" onclick="document.all.menu3.style.display = 'none';">

<font size="-1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="littledash.gif">&nbsp;<a href="http://www.cnn.com">CNN</a></font><br>
<font size="-1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="littledash.gif">&nbsp;<a href="http://www.abcnews.com">ABC News</a></font><br>
<font size="-1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="littledash.gif">&nbsp;<a href="http://www.usatoday.com">USA Today</a></font><br>
<font size="-1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="littledash.gif">&nbsp;<a href="http://www.marketwatch.com">CBS Market Watch</a></font>

</span>
</td>
</tr>

</table>
</body> 

</html>

Overview

Although you wouldn't know it from looking at the script's demo page, the script's display is housed in a three-row, three-cell table. (We'll see later that the table 'scaffolding' is unnecessary.) Like the Script Tips #76-78 Script's click layer, each table cell holds
(a) an initially visible heading and
(b) an initially invisible set of links.
Unlike the click layer links, the table cell links do not occupy any space on the page; this is because the table cell links are wrapped in span elements whose CSS display property is set to none:

#menu1 { display: none; }
#menu2 { display: none; }
#menu3 { display: none; }
/* These rules can be more compactly written as:
#menu1, #menu2, #menu3 { display: none; }
CSS selector grouping is discussed here. */

The table cell headings are themselves wrapped in span elements equipped with onmouseover/onmouseout commands that drop down their links, for example:

<span onmouseover="document.all.menu1.style.display = 'block';" onmouseout="document.all.menu1.style.display = 'block';">
<font size="-1"><b><img src="littlex.gif">&nbsp;Goodies Tutorials</b></font></span>

Moving the mouse cursor over the Goodies Tutorials heading pops out the links in the id="menu1" span element by setting the menu1 CSS display property to block, which, as you might guess, displays the links with a block-level rendering, e.g., newlines (\ns) are inserted before and after the links. Contra the tutorial, the display block value does not serve to negate the menu1 display none value that is set in the script's style element.

The onmouseout attribute is redundant; if we delete it, the link menu won't go away if the mouse cursor is moved away from the menu.

In turn, each link-containing span element is equipped with an onclick command that pulls up its link menu when the user clicks in the span region anywhere but on the links, e.g.:

<span id="menu1" onclick="document.all.menu1.style.display = 'none';">

From MSIE-specific to cross-browser

You have probably figured out that it is the script's use of the all collection in the onmouseover/onmouseout/onclick commands that makes it MSIE-specific. We first encountered the all collection in the color creation script of Script Tips #65-67 and correspondingly discussed it in detail in Blog Entry #85. Microsoft's all collection page shows that the all syntax here is not quite standard: the menu# span elements should be referenced with document.all("menu#") and not document.all.menu#.

Anyway, all that is necessary to make the script work with other (current) browsers is to replace each document.all.menu# reference with a document.getElementById("menu#") reference - it's that simple.

The Microsoft menu interface

Joe's menus and the Microsoft menus that inspired them actually have different user interfaces. Both sets of menus preface their headings with small 'plus' (The small plus image) images and their links with small 'minus' (The small minus image) images. However, Microsoft's menus are not displayed/vanished via a mouseevent interface; rather, the Microsoft menus
(a) are dropped down by clicking on the heading The small plus image images, which are 'flipped' to the The small minus image images when the links display; and
(b) are pulled up by clicking on the heading The small minus image images, which toggle back to the The small plus image images when the links vanish.

Below I'll show you how to replace Joe's mouseevent interface with the Microsoft interface if you'd like to do that, but let me get some style stuff out of the way first...

A style sheet for the "Drop-Down Menu" tutorial script

body { background-color: white; }
table { border: 0; }
td { width: 200px; vertical-align: top; }
h4 { display: inline; }
#menu1, #menu2, #menu3 { position: relative; left: 25px; }
a:link { color: black; text-decoration: none; }
a:hover { color: blue; text-decoration: underline; }

Comments
The HTML 4 Index of Attributes shows that the bgcolor attribute of the body element, the align attribute of the table element, and the width attribute of the td element are all deprecated.
• My preference is to simply remove the table element's align="left" attribute and not replace it with any style declarations (e.g., float:left;) because the script's table is by default left-justified.
• I find that simply removing the td elements' valign="top" attributes does not affect the script's effect (so the td vertical-align:top; declaration is probably unnecessary).
• The style sheet doesn't have a rule replacing the <font size="-1"> elements, which produce text too small for my tastes.
• We will below mark up the boldened menu headings with h4 elements, whose default block-level rendering is switched to inline by the display:inline; declaration above (technically, this declaration puts the h4 elements in the same CSS line boxes that hold the The small plus image images). If you're uncomfortable with skipping the h1-h3 heading levels, then you can alternatively mark up the headings with strong elements, which are inline by default and are typically rendered with a bold font weight.
• Joe gives each menu link a five-space indent via a series of &nbsp; entity references, whose effect is smoothly reproduced by applying position:relative;left:25px; declarations to the menu# span elements.
• The menu# span elements' initial display:none; properties (vide supra) will be written in a script element (vide infra).

Click it

We're now ready to supplant Joe's mouseevent menu interface with the Microsoft click interface described earlier. We begin by recoding the first table cell as:

<td>
<img id="image1" src="littlex.gif" onclick="displayx(1);" /> <h4>Goodies Tutorials</h4>
<span id="menu1">
<img src="littledash.gif" /> <a href="http://www.htmlgoodies.com/tutorials/frames/">Frames</a><br />
<img src="littledash.gif" /> <a href="http://www.htmlgoodies.com/tutorials/tables/">Tables</a><br />
<img src="littledash.gif" /> <a href="http://www.htmlgoodies.com/beyond/dhtml/">DHTML</a><br />
<img src="littledash.gif" /> <a href="http://www.htmlgoodies.com/tutorials/forms/">Forms</a>
</span></td>

The script's original href values for the Frames, Tables, DHTML, and Forms links are not current but are functional; the code above updates them. Joe's littlex.gif (The small plus image) and littledash.gif (The small minus image) images can be accessed at http://www.htmlgoodies.com/images/littlex.gif and http://www.htmlgoodies.com/images/littledash.gif, respectively. Lastly, there's no need to prevent line-wrapping before the menu heading, so I've replaced the &nbsp; reference between the littlex.gif image and the heading with an ordinary space.

The second and third table cells can be recoded analogously. Next, place the following script element after the third td element:

<script type="text/javascript">

document.getElementById("menu1").style.display = "none";
document.getElementById("menu2").style.display = "none";
document.getElementById("menu3").style.display = "none";

function displayx(num) {
if (document.getElementById("menu" + num).style.display == "none") {
document.getElementById("menu" + num).style.display = "block";
document.getElementById("image" + num).src = "littledash.gif"; }
else {
document.getElementById("menu" + num).style.display = "none";
document.getElementById("image" + num).src = "littlex.gif"; }
}
</script>

A single displayx( ) function handles all three menus. In changing the menu# displays from none to block, I find that the original menu1, menu2, menu3 { display:none; } style rule is not recognized by the script element; the displayx( ) if condition returns false on the first click because its document.getElementById("menu"+num).style.display expression evaluates in this case to an empty string (the expression is subsequently set to none by the else block, after which the script works normally). At least on my computer, the if condition only returns true on the first click if the menu# displays are set to none in the script element.

And that'll do it - try it out in the div below:

The small plus image

Goodies Tutorials

The small minus image Frames
The small minus image Tables
The small minus image DHTML
The small minus image Forms
The small plus image

Great Sites

The small minus image The Straight Dope
The small minus image Project Gutenberg
The small minus image HowStuffWorks
The small minus image Snopes.com
The small plus image

News Sites

The small minus image CNN
The small minus image New York Times
The small minus image BBC
The small minus image Agence France-Presse


(Regarding the Great Sites menu, you'll get no argument from me that Cecil Adams' The Straight Dope is indeed a great Web site, but as for the Van Halen News Desk, Dave TV, and Hair Club for Men, well, I thought other link choices might be more appropriate. I have also restocked the News Sites menu with some different links - why not?)

In looking at the indented links, perhaps you are thinking, "Can't we use list elements to code these menus?" Indeed we can, and we'll restructure the script with a nested list 'chassis' in the next post.

reptile7


Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)