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)
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; }
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>
<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 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.
*/
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 littlex.gif image that will mark the outerlist menu headings is available here and the 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 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
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>
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";
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.,
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:
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 / image markers or on the headings themselves:
- Goodies Tutorials
- Great Sites
- News Sites
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"> Goodies Tutorials</b></font></span><br> <span id="menu1" onclick="document.all.menu1.style.display = 'none';"> <font size="-1"> <img src="littledash.gif"> <a href="/tutors/frame1.html">Frames</a></font><br> <font size="-1"> <img src="littledash.gif"> <a href="/tutors/tbl.html">Tables</a></font><br> <font size="-1"> <img src="littledash.gif"> <a href="/beyond/dhtml.html">DHTML</a></font><br> <font size="-1"> <img src="littledash.gif"> <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"> Great Sites</b></font></span><br> <span id="menu2" onclick="document.all.menu2.style.display = 'none';"> <font size="-1"> <img src="littledash.gif"> <a href="http://www.straightdope.com">The Straight Dope</a></font><br> <font size="-1"> <img src="littledash.gif"> <a href="http://www.vhnd.com">Van Halen News Desk</a></font><br> <font size="-1"> <img src="littledash.gif"> <a href="http://www.dave-tv.com">Dave TV</a></font><br> <font size="-1"> <img src="littledash.gif"> <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"> <font size="-1">News Sites</font></b></span><br> <span id="menu3" onclick="document.all.menu3.style.display = 'none';"> <font size="-1"> <img src="littledash.gif"> <a href="http://www.cnn.com">CNN</a></font><br> <font size="-1"> <img src="littledash.gif"> <a href="http://www.abcnews.com">ABC News</a></font><br> <font size="-1"> <img src="littledash.gif"> <a href="http://www.usatoday.com">USA Today</a></font><br> <font size="-1"> <img src="littledash.gif"> <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';">
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' () images and their links with small 'minus' () 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 images, which are 'flipped' to the images when the links display; and
(b) are pulled up by clicking on the heading images, which toggle back to the 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 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 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>
<span id="menu1">
</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 () and littledash.gif () 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 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:
Goodies Tutorials |
Great Sites |
News Sites |
(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
Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)