reptile7's JavaScript blog
Wednesday, May 28, 2008
 
The Script Tips #92-93 Script: Coda
Blog Entry #114

At the conclusion of his "Post the Tables" article, Joe pronounces the pop-up tables script of HTML Goodies' JavaScript Script Tips #92-93 a pretty clever piece of work, and I would agree. The script's use of loops and arrays to assemble the initial display table and the pop-up tables - in particular, its loop indexing that ties together the Titles headers, the Text child arrays, and the pop-up tables - is definitely pretty creative. "But this requires the script's HTML and JavaScript to be commingled," you might legitimately object. Yeah, that bothers me too somewhat; we discussed the desirability of keeping HTML and JavaScript separate in the first section* of Blog Entry #86. (*Not mentioned in this section: mixing HTML and JavaScript can also cause validation problems - see the "Validation" section below.) If it were my résumé, I would certainly also want to have 'longhand' versions of the script's tables on hand, i.e., to have all of the table HTML written out in a normal manner, for whatever reason; for example, here's the Testing pop-up table code in longhand:

<table border="2" cellspacing="4" cellpadding="1" style="background-color:yellow;color:black;">

<tr style="background-color:black;color:yellow;">
<th>Language</th><th>Years Used</th><th>Last Used</th></tr>

<tr style="font-weight:bold;">
<td>QA Partner (4Test)</td><td style="text-align:center;">6</td><td>Current</td></tr>

<tr><td>DTM</td><td style="text-align:center;">3</td><td>1986</td></tr>

<tr><td>WinRunner</td><td style="text-align:center;">0.25</td><td>1997</td></tr>
</table>

A demo and a style sheet

OK, it's time we got to that cross-browser demo, wouldn't you say? In the div below, move your mouse cursor over a heading in a left-hand cell to pop up its corresponding pop-up table (depending on your browser, the pop-up table may pop up if the mouse cursor is anywhere in the cell and not just over its text):

Welcome to the pop-up tables demo div.


The demo script makes use of the following style identifiers:
(1) id="div1" for the div element that establishes the containing block for the pop-up tables (I've thrown out the 'div2' div elements);
(2-3) class='invisibleTable imYellow' for the pop-up tables themselves;
(4) class='topRow' for the first (header) rows of the pop-up tables;
(5) class='centerMe' for the td cells of the middle ("Years Used") columns of the pop-up tables;
(6) class='boldMe' for the bolded pop-up table rows and the "Language" data thereof appearing in the right-hand cells of the initial display table;
(7) id="visibleTable" for the initial display table;
(8) class='visibleTh' for the left-hand (header) cells of the initial display table;
(9) class='visibleTd' for the right-hand ("Language"-data) cells of the initial display table; and
(10) class='visibleData' for the span elements (replacing the original font elements) holding the text in the right-hand cells of the initial display table.

Here's the demo CSS:

body { background-color: white; }

#div1 { position: relative; }

.invisibleTable { display: none; position: absolute; left: 230px; top: 5px; }
/* A bit further to the right than in the original script, per my preference */

.imYellow { background-color: yellow; color: black; }

.topRow { background-color: black; color: yellow; /* text-align: center; */ }
/* The browsers on my computer initially center th text and left-justify td text. */

.centerMe { text-align: center; }

.boldMe { font-weight: bold; }

#visibleTable, .visibleTh, .visibleTd { border: 1px solid green; }
/* Why green? Why not? */

.visibleTh { text-align: right; }

.visibleData { color: blue; font-size: 16px; font-family: Arial, sans-serif; }

The above rules replace all of the script's presentational elements and attributes except for a few of the table element attributes; in particular, I've left the cellspacing attributes in place because MSIE 5 for the Macintosh supports neither the CSS border-collapse property nor the CSS border-spacing property. (It is left to you to exchange the pop-up tables' border and cellpadding attributes for corresponding style rules in the event that you feel a burning need to do so.)

Validation

The original Script Tips #92-93 Script begins with a

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

document type declaration, which I left intact in my modified demo script, and I wondered, "Is the Script Tips #92-93 Script document actually valid?" In fact, neither the original Script Tips #92-93 Script nor my demo script passes W3C validation. After subtracting the </a characters from the document.write( ) command holding the eHide( ) function call, the original Script Tips #92-93 Script can be brought into conformance with the HTML 4.01 Transitional DTD by making the following changes:

(1) Add a title element.

<title>Welcome to the Script Tips #92-93 Script Demo Page.</title>

The content model of the head element shows that, for a valid document, the head element must contain a title element, a point I shamefully did not make in my discussion of (X)HTML validation in Blog Entry #92.

(2) Specify a character encoding via a meta element.

<meta http-equiv="Content-Type" content="text/html;charset=utf-8">

FYI: HTML validation will fail if a 'terminated' <meta /> element is used here.

(3) Equip the script element start-tags with type attributes (even in the Transitional DTD, the type attribute of the script element has a #REQUIRED default value designation).

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

Of course, conformance with the HTML 4.01 Strict DTD requires removal of the deprecated language="javascript" attribute(s). BTW, the language attribute of the script element has an #IMPLIED default value designation in the Transitional DTD, so removing it won't invalidate the document vis-à-vis the Transitional DTD.

(4) Regarding the HTML end-tags that appear in the contents of the document body script elements: the slash characters of these tags must be preceded by backslashes (this is an SGML thing and relates to the script element's content model (vide infra)), e.g.:

document.write("<\/div>");

We previously commented on the 'illegality' of unescaped end-tags in script elements at the end of Blog Entry #58.

(5) Regarding the initial display table: the content model of the table element shows that the table element cannot have a script element child; for validation purposes, use the table's script element to (also) write the table start-tag and end-tag, i.e.:

<script type="text/javascript">
document.write("<table border='0' cellspacing='4'>");
...for loops that write the table's rows/cells...
document.write("<\/table>");
</script>

With the above changes in place, bringing the Script Tips #92-93 Script into conformance with the HTML 4.01 Strict DTD is then a simple matter of
(a) throwing out the script element language attributes,
(b) replacing the body element's bgcolor="#ffffff" attribute with a corresponding style rule, and finally
(c) changing the document type declaration** to
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">.
(**As noted in Blog Entry #92, a system identifier must be included in the declaration for XHTML validation but is not needed for HTML validation.)

You might think that we'd need to get rid of the deprecated HTML (the font elements and the table/tr/th bgcolor attributes) in the document body script elements before validating the Script Tips #92-93 Script against the Strict DTD, but we don't. The script element has a "CDATA" content model designation; as a result, the validator regards script element content including stringified start-tags

document.write("<font color=\"#0000ff\" size=\"3\" face=\"Arial\">");

as "raw text" and not markup, with one exception: end-tags (stringified or not)

document.write("\n</font></a></td></tr>");

are perceived as markup, specifically, a </[a-zA-Z] character sequence in a script element is seen as the end of the script element, and thus such sequences must be escaped with backslashes as shown earlier.

For XHTML-compliance, the document body script element code should be externalized as it contains < characters aplenty.

That's it for the Script Tips! What next? We'll briefly consider some future directions for this blog in the next post.

reptile7

Sunday, May 18, 2008
 
The Script Tips #92-93 Script, Part 5
Blog Entry #113

Getting the script to work with Netscape

Per the "Getting the script to work with MSIE" section of Blog Entry #109, let's say that we
(a) subtract the </a characters from the document.write( ) command holding the eHide( ) function call, and
(b) replace the eShow( )/eHide( ) document.all(name) references with document.getElementById(name) references.
The Script Tips #92-93 Script should then work OK with Netscape 6+, right? But this is what I get with Netscape 7.02 on my computer when I mouseover, for example, the Word, WordPerfect, TPU, EEL, SNOBOL, Postscript text in the right-hand cell of the eighth row of the initial display table:

Problematic pop-up table flowing with Netscape 7.02

Pretty ugly, huh? Here are the problems that arise:

(1) Recalling that the div2 div parent of each pop-up table has a width value of 350px, the CSS background-color and border-style properties for the part of the pop-up table that 'overflows' (extends beyond the right edge of) the div2 div are set to their 'initial' values: transparent and none, respectively; in practice, the overflowing table portion appears to have a white background (the body element's background color shows through) and its border disappears.

(2) When the pop-up table pops up, the initial display table shifts downward - note in the above image that the top edge of the initial display table has been pushed below the top edge of the pop-up table (go here to see the correct table positions) - as though Netscape has added an empty line box to either the div1 or div2 div.

Why do MSIE and Netscape overflow the pop-up tables differently? The answer seems to lie with the pop-up table width.

What determines the width of an HTML table? The table element has an undeprecated width attribute with an #IMPLIED default value designation; #IMPLIED means 'optional' in a practical sense. The pop-up tables are not given a width attribute; for this situation, the HTML 4.01 Specification only says, In the absence of any width specification, table width is determined by the user agent. Not very satisfying, is it?

Fortunately, Section 17.5.2 of the CSS 2.1 Specification discusses table width in some detail. The pop-up tables are "absolutely positioned, non-replaced elements", whose widths are generally determined by rules given in Section 10.3.7 of the CSS 2.1 Specification; Section 17.5.2 is supposed to "override" the Sections 10.3.x pertaining to non-replaced elements, however.

The CSS table-layout property is not set for the pop-up tables and thus takes its initial value of auto; consequently, the pop-up tables should be laid out according to the "automatic table layout" algorithm; at the same time, the W3C says, UAs [user agents] are not required to implement this algorithm to determine the table layout in the case that table-layout is auto; they can use any other algorithm even if it results in different behavior.

The following Section 17.5.2 paragraph is applicable to the pop-up table width:
If the table or inline-table element has width: auto, the table width used for layout is the greater of the table's containing block width and MIN. However, if the maximum width required by the columns plus cell spacing or borders (MAX) is less than that of the containing block, use MAX.
Let's flesh this out one step at a time.

(1) In the previous entry, I argued that the script's eShow( ) function should set a pop-up table's display value to block and not inline. I neglected to point out that the CSS display property has specialized table and inline-table values for flowing tables in a block-level and inline manner, respectively; relatedly, the table element is given a display: table style in the CSS 2.1 Specification's "Default style sheet for HTML 4" appendix. Recasting the eShow( ) function as

function eShow(name) { document.getElementById(name).style.display = "table"; }

solves the pop-up tables' background-color/border-style overflow problems but does not solve the 'downward shift' problem when using Netscape.

(2) The CSS width property is not set for the pop-up tables and thus takes its initial value of auto.

(3) As noted two entries ago, the containing blocks for the pop-up tables are established by the tables' absolutely positioned div2 div element parents, whose CSS width is specifically set to 350px.
(Interesting side note: somewhat surprisingly, the HTML div element does not have a width attribute.)

(4) MIN means the minimum width required by all the columns plus cell spacing or borders; complementarily, MAX, defined above, is the maximum width required by the columns plus cell spacing or borders. In determining the width of a table, the browser should lay out
(a) a MIN table by trying all possible line breaks in flowing the table's td/th content, and
(b) a MAX table by formatting the content without breaking lines other than where explicit line breaks occur.
(Some tables will not have different MIN and MAX forms.)
For example, the

<td><b>QA Partner (4Test)</b></td>

cell in the Testing pop-up table would be flowed as

QA
Partner
(4Test)

in a MIN table and as

QA Partner (4Test)

in a MAX table.

The MAX widths of the pop-up tables vary, and the pop-up table with the largest MAX width is in fact (by virtue of the QA Partner (4Test) cell) the Testing table, whose MAX width is approximately 335 pixels, which is smaller than the div2 div width. Consequently, Section 17.5.2 would recommend that all of the pop-up tables be given MAX widths, and MSIE 5.1.6 does just that on my computer, as does Netscape 7.02 if table is assigned to the tables' display property in the eShow( ) function.

With a display = "inline" or display = "block" eShow( ) assignment, however, Netscape 7.02 does not use MAX widths for the pop-up tables; rather, in these cases Netscape seems to follow the third rule of Section 10.3.7 (width = right = auto; leftauto) and give the tables "shrink-to-fit" widths according to the following formula:

shrink-to-fit width = min(max(preferred minimum width, available width), preferred width)

Section 10.3.7's "preferred minimum width" corresponds to Section 17.5.2's MIN width; its "preferred width" corresponds to the MAX width. The "available width" is obtained by solving the following equation for width after right is set to 0:

left + margin-left + border-left-width + padding-left + width + padding-right + border-right-width + margin-right + right + scrollbar width = width of containing block

left, set by the pop-up table elements' inline style attribute, is 200px.
margin-left, margin-right, padding-left, and padding-right all take their initial values of 0.
border-left-width and border-right-width, set by the pop-up table elements' border attribute, are both 2px.
• The pop-up tables do not have right scrollbars and thus the scrollbar width is 0; as noted above, right (initially auto) is 0 and the width of the containing block is 350px.

Plugging in the above values and solving for width gives an available width of 146px. Now, let's turn to the inner part of the shrink-to-fit width expression:

max(preferred minimum width, available width)

For all ten pop-up tables, the preferred minimum width is greater than the available width. The pop-up tables with the smallest preferred minimum width are the Assembly and Statistical tables; for both of these tables, the preferred minimum width is approximately 195 pixels:

The Assembly pop-up table with a preferred minimum width The Assembly pop-up table with a preferred minimum width - as in the image at the outset of this post, note the line breaks between "Years" and "Used" and between "Last" and "Used".

The outer part of the shrink-to-fit width expression then becomes:

min(preferred minimum width, preferred width)

By definition, the preferred minimum width can never be greater than the preferred width (for some tables, these widths will be equal), and for all ten pop-up tables, the preferred minimum width is less than the preferred width. It follows that Section 10.3.7 would recommend that all of the pop-up tables be given preferred minimum (MIN) widths, and that's what I see with Netscape 7.02 on my computer.

For the shrink-to-fit width to equal the preferred width, it is necessary for the pop-up tables' available widths to be greater than both their preferred minimum widths and their preferred widths, i.e.,
(a) max(preferred minimum width, available width) must give the available width, and
(b) min(available width, preferred width) must give the preferred width.
As noted above, the pop-up table with the largest preferred width is the Testing table. In turn, for the pop-up tables' available widths to be greater than the Testing table's preferred width (335 pixels), it is necessary for the width of the tables' containing blocks to be at least 204 pixels larger than the Testing table's preferred width (cf. the available width formula above). And indeed, I find that increasing the div2 div width value to 545px (or higher) successfully imparts preferred (MAX) widths to all of the pop-up tables (all th/td line breaks are removed) and sorts out their overflow problems when using Netscape. Alternatively (and preferably IMO), the div2 divs can be removed, and the div1 div, with its effective width value of 100%, can be used to establish the containing block for the pop-up tables.

I find that the downward shift problem is solvable via either
(a) bringing the initial display table into the div1 div (so that it's a sibling of the script element that writes the pop-up tables)

<div id="div1" style="position:relative;">
<script type="text/javascript"> ...pop-up table code... </script>
<table border="0" cellspacing="4"> ...initial display table code... </table>
</div>

or (b) using the visibility property instead of the display property for hiding and showing the pop-up tables as described in the previous entry.
I admit that these are trial-and-error solutions and I don't have explanations for them.

Both solutions place the pop-up tables 5 pixels below their MSIE position (as though the tables were inheriting the 5px top value of their div2 div parents - the CSS top property is in fact not inherited); to the extent that this bothers you, the MSIE and Netscape positions can be harmonized by adding a top: 0px declaration to the pop-up table elements' style attribute.

In the next entry, we'll wrap up our discussion of the Script Tips #92-93 Script with a demo, a style sheet, and some final commentary.

reptile7

Thursday, May 08, 2008
 
The Script Tips #92-93 Script, Part 4
Blog Entry #112

Showing and hiding the pop-up tables

So, just how does the Script Tips #92-93 Script pop up its pop-up tables, anyway?

We begin by noting that each pop-up table is given a Titles element identifier - id="General Programming" (Titles[0]) for the first table, id="Testing" (Titles[1]) for the second table, id="Windows" (Titles[2]) for the third table, and so on:

for (var j = 0; j < Titles.length; ++j) {
name = Titles[j];
document.write("<div style=\"position:absolute; top:5px; width:350px;\">" +
"<table id=\"" + name + "\" cellpadding='1' cellspacing='4' border='2' " +
"bgcolor=" + bgColor + " style=\"display:none; position:absolute; left:200px;\">"); // etc.
/* Is it necessary to variabilize Titles[j] as name? Not at all. */

The script's eShow( ) and eHide( ) functions, true to their names, respectively pop up and vanish each pop-up table:

function eShow(name) { document.all(name).style.display = "inline"; }
function eHide(name) { document.all(name).style.display = "none"; }

The eShow( ) and eHide( ) functions are respectively called by onmouseover and onmouseout commands carried by the anchor element parents of the font elements that hold the blue text of the right-hand cells of the initial display table (go here to see the initial display table):

document.write("</td><td><a onmouseover=\"eShow(&quot;" + Titles[i] + "&quot;)\" ");
document.write("onmouseout=\"eHide(&quot;" + Titles[i] + "&quot;)\">");
document.write("<font color=\"#0000ff\" size=\"3\" face=\"Arial\">"); // etc.

We noted in the previous entry that the pop-up tables are respectively housed in separate parent div elements, to which we gave a class='div2' identifier. In his "Post the Tables" article, Joe incorrectly states three times that the eShow( )/eHide( ) functions act on a table's div element container. Actually, as shown by the above code,
(a) the div2 divs aren't 'hidden' in the first place, and
(b) the onmouseover/onmouseout commands send an id value (Titles[i]) for a table to eShow(name)/eHide(name), which then act directly on the table (document.all(name)) itself.

Contra the "Post the Tables" discussion of the pop-up process, it is not true that [t]he table is created each time the mouse passes over, not before; all ten pop-up tables are created before the user mouseovers any text (and before the script document has loaded, for that matter).

Moreover, Joe insinuates that the pop-up tables pop up when the user mouseovers the Titles headers in the left-hand cells of the initial display table when he says, Depending on which word is passed over, that value is passed to the [eShow( )/eHide functions] - also not true. But now that I think about it, I like the idea of shifting the onmouseover/onmouseout commands to the left-hand cells in some way, say, to a span element wrapper for the Titles headers.

The CSS display property of the pop-up tables is initially set to none; eShow( ) switches the table display value to inline whereas eHide( ) toggles it back to none. For showing the pop-up tables, a block display value would seem to be a more natural choice for the following reasons:
(1) The pop-up tables are absolutely positioned; according to Section 9.4.1 of the CSS 2.1 Specification, absolutely positioned elements...establish new block formatting contexts.
(2) In the absence of positioning, tables are typically rendered as block-level elements anyway.
(3) Perhaps most fundamentally, there's no point in having the pop-up tables "participate" in an "inline formatting context" because there's no other content in the div2 divs for flowing next to them.
In practice, I find that both inline and block are suitable values for the eShow( ) document.all(name).style.display assignment.

Regarding eShow( ) and eHide( ), Joe states that these functions set the [table] to either visible or hidden. When the mouse passes over, [it's] visible. My 'radar went off' when I read this; visible and hidden are values for the CSS visibility property*, not for the display property. But I subsequently thought, "Given that the pop-up tables are taken out of the 'normal flow' via their absolute positioning, does it really matter whether they take up space or not in their 'invisible' state? [A visibility: hidden element and a display: none element are both invisible; however, a visibility: hidden element occupies its normal (visible) area, like an empty placeholder, on the page, whereas a display: none element doesn't occupy any space on the page.] Could we use the visibility property here if we wanted to?" In the event, I find that the tables' initial display: none declaration, the eShow( ) display = "inline" assignment, and the eHide( ) display = "none" assignment can be smoothly replaced by visibility: hidden, visibility = "visible", and visibility = "hidden", respectively.

(*FYI: visible and hidden are also values for the CSS overflow property. We'll discuss the pop-up tables' overflow of their div2 containing blocks in the next post.)

Finally, the quote formatting of the onmouseover/onmouseout commands deserves some comment. The onmouseover/onmouseout commands appear in document.write( ) parameters that are delimited with double quotes; consequently, the script's author has delimited the eShow( )/eHide( ) function calls with \" escaped double quotes - I myself would have used single quotes for this purpose, but to each his own. The eShow( )/eHide( ) arguments, Titles[i], are also quoted, specifically, they are surrounded (indirectly) by &quot; character entity references that also represent double quotes.

You wouldn't think (or at least I wouldn't think) that it'd be necessary to quote the Titles[i] arguments; the Titles elements should be perceived as strings by the browser. Indeed, if a

window.alert(typeof Titles[i]);

command is inserted after the document.write( ) commands carrying the anchor element start-tag(s), then ten alert( ) boxes all displaying string dutifully pop up. However, my attempts to pop up the pop-up tables after subtracting the &quot; references were unsuccessful; in this regard, here's what happens when I mouseover, for example, the MS-DOS, NDOS, DCL, JCL, UNIX text in the right-hand cell of the last row of the initial display table:

(A) MSIE 5.1.6 does not send Titles[9], Command, to eShow( ) but inexplicably sends instead a reference to the pop-up table whose id value is Command:

function eShow(name) {
window.alert(name); // Displays [object TABLE]
window.alert(name.id); // Displays Command
document.all(name).style.display = "inline"; /* Throws a 'document.all(...).style' is not an object runtime error */ }

(B) Netscape 7.02 simply throws a Command is not defined error, i.e., Netscape evaluates the Titles[9] expression but still perceives the return, Command, as a variable lacking a value.

In sum, the &quot; references do need to be there (other quote formattings for the eShow( )/eHide( ) function calls are possible, but we're not going to get into them here). Alternatively, I find that both MSIE and Netscape will uneventfully pass an unquoted Titles index

document.write("</td><td><a onmouseover='eShow(" + i + ")' ");
document.write("onmouseout='eHide(" + i + ")'>");

to eShow( ) and eHide( ), which can then be reformulated as:

function eShow(myIndex) {
myTable = document.getElementById(Titles[myIndex]);
myTable.style.display = "block"; }
function eHide(myIndex) {
myTable = document.getElementById(Titles[myIndex]);
myTable.style.display = "none"; }

Netscape execution

We'll take up this topic and also roll out a cross-browser demo in the following entry.

reptile7


Powered by Blogger

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