reptile7's JavaScript blog
Saturday, August 25, 2007
Before and After Color Science
Blog Entry #86

Having analyzed the Script Tips #65-68 Scripts in the previous two entries, we will in this post revamp the Script Tips #65-67 Script in the following ways:
(1) We'll separate some (not all) of the script's HTML and JavaScript.
(2) We'll take the presentational stuff out of the script's HTML and put it in a style block.
(3) We'll write the Red, Green, and Blue options with a for loop - in doing so, we'll replace the gargantuan menu string with 10 lines of code.
Most of our remarks today will apply to the Script Tip #68 Script as well.

Separating HTML and JavaScript

For a block of code containing HTML and JavaScript, it is in general a good idea to keep the HTML and JavaScript separate:
• At the very least, both HTML and JavaScript are easier to read if they are free of one another.
• Typically, standalone HTML and JavaScript are maintained/modified more easily than is an HTML-JavaScript mixture.
• Also, externalizing a JavaScript script in a separate .js file* will allow it to be used by more than one document, if this is desired.
• From an accessibility standpoint, a separation of HTML and JavaScript facilitates the creation of alternative-content pages for user agents that do not or are configured to not execute JavaScript.
As you know, analogous considerations encourage a separation between HTML structure and presentation.
(*With respect to XHTML-compliance, the W3C doesn't have a problem with non-external scripts as long as they don't contain the metacharacters < and &.)

However, some HTML-JavaScript mixtures don't lend themselves very well to a clean resolution into HTML and JavaScript units. Consider, for example, the guitar chord chart script of Script Tips #56-59, which creates a 6×7 radio button grid via a pair of nested for loops. To achieve a complete separation of HTML and JavaScript in this case, we'd have to code individually all those radio buttons, and you wouldn't want to do that, would you? Didn't think so.

Two entries ago we noted that the Script Tips #65-67 Script conflates its HTML and JavaScript in a single script element. In this case, a complete separation of HTML and JavaScript would require us to code individually the 768 options of the red/green/blue selection lists, and we're certainly not going to do that, either. (My attempts to create these options with new Option( ) constructor statements, discussed later, met with mixed success.) But we can at least get the table code and the rest of the ColorMix form code out of the script element:

<!-- The table element was originally equipped with cellpadding="0" and cellspacing="0" attributes, whose removal does not noticeably affect the table's display. -->
<form name="ColorMix">

<tr><td id="box" colspan="4"></td></tr>
<td class="quarter">Red: <select name="red" size="1" onchange="mix(this.form);">
<script type="text/javascript">document.write(threeDigitMenu( ));</script></select></td>

<td class="quarter">Green: <select name="green" size="1" onchange="mix(this.form);">
<script type="text/javascript">document.write(threeDigitMenu( ));</script></select></td>

<td class="quarter">Blue: <select name="blue" size="1" onchange="mix(this.form);">
<script type="text/javascript">document.write(threeDigitMenu( ));</script></select></td>

<td class="quarter"><input name="code" maxlength="7" size="10" value="#000000" />

The four cells of the second table row have each been given a class="quarter" style identifier; we'll get into some CSS momentarily.

We can shorten the mix( ) function assignment statements a bit by passing to mix( ) this.form, a reference to the ColorMix form:

function mix(myForm) {
document.getElementById("box").style.background = "#" +[].value +[].value +[].value;

myForm.code.value = "#" +[].value +[].value +[].value; }

The Red/Green/Blue options will be written by a threeDigitMenu( ) function detailed in the "option loop" section below.

A style block for the Script Tips #65-67 Script

table { border: 0px; width: 100%; }
td#box { background-color: black; height: 25px; }
td.quarter {
color: black;
font-family: sans-serif, serif;
font-size: 16px;
font-weight: bold;
width: 25%; }

Neither the border attribute nor the width attribute of the table element is deprecated, but these attributes/properties belong in the style block, so we'll put them there.

The Red/Green/Blue labels are given a basic black color, but you might prefer colored Red, Green, and Blue labels, and that's simple enough to arrange:
(a) To the td element start-tags of the first three cells of the second table row, add id="redcell", id="greencell", and id="bluecell" attributes, respectively.
(b) In the style block, subtract or comment out td.quarter's color:black; declaration and then add the following rules:

td#redcell { color: red; }
td#greencell { color: lime; }
td#bluecell { color: blue; }
/* These rules will not color the numbers on the Red/Green/Blue options. */

In the Script Tips #65-68 Scripts, the Red/Green/Blue labels are marked up with a face='arial, geneva, times new roman' attribute. Arial and Geneva are both sans-serif fonts, whereas Times New Roman is a serif font, and I have accordingly given the style block a td.quarter { font-family:sans-serif, serif; } rule. There seems to be a general preference on the Web for sans-serif fonts, understandably so given their clean appearance.

The Red/Green/Blue labels are also marked up with a size='2' attribute. On my computer, size='2' is equivalent to a font-size:13pt; CSS declaration. My style block's td.quarter { font-size:16px; } rule reflects
(a) my own preference for a larger font size, and
(b) the W3C's recommendation that Web authors use relative length units and not absolute length units.

An option loop

As noted earlier, the Script Tips #56-59 Script uses two for loops to create a set of radio buttons, and I thought, "There must be some way to use a for loop to generate the menu option string in the Script Tips #65-68 Scripts." This example on the JavaScript 1.3 Client-Side Reference's option object page seemed to offer the general approach I was looking for. After some experimentation, I found that the following code, when put in or referenced by a script element placed after the ColorMix form element, would slowly (>30 seconds) generate the Red/Green/Blue options when using MSIE but not when using Netscape:

optionarray = new Array( );

for (i = 255; i >= 0; i--) {
var hexvalue = i.toString(16);
if (/^\w$/.test(hexvalue)) hexvalue = "0" + hexvalue;
var threedigit = i;
if (/^\d{2}$/.test(threedigit)) threedigit = "0" + threedigit;
if (/^\d$/.test(threedigit)) threedigit = "00" + threedigit;

optionarray[i] = new Option(threedigit, hexvalue);[i] = optionarray[i];[i] = optionarray[i];[i] = optionarray[i];[0].selected = true;[0].selected = true;[0].selected = true; }

This script creates an option object array, optionarray[ ], whose elements are assigned, one by one, to the options[ ] properties of the red/green/blue selection lists. (Without getting into the details, Netscape did create the optionarray[ ] array but would not assign its elements to the red/green/blue options[ ] arrays.)

The loop counter i doubles as an optionarray[ ]/options[ ] index number and decrements from 255 to 0. For reasons beyond my understanding, a for (i = 0; i < 256; i++) loop would generate all three option lists but only the last-in-source-order list (for the code above, the blue list) was functional.

Each option is created by a constructor statement as opposed to an HTML option element:

optionarray[i] = new Option(threedigit, hexvalue);

The text (content) of each option is represented by the variable threedigit. Initially, i is assigned to threedigit, which is sufficient for the 255threedigit100 options. For the 99threedigit10 and 9threedigit0 options - ranges for which threedigit matches the /^\d{2}$/ and /^\d$/ regular expressions, respectively - a pair of if statements prepend "0" and "00" to threedigit, respectively; the leading zeros are of course unnecessary and thus these if statements can be removed, if desired.

The value of each option is represented by the variable hexvalue. floR in the Netherlands helpfully points out that decimal numbers are easily converted to their hexadecimal equivalents via the toString( ) method of the core JavaScript Number object, in this case:

var hexvalue = i.toString(16);
// 16 is the radix (base) of the outputted number string.

For the fhexvalue0 options - a range for which hexvalue matches the /^\w$/ regular expression - an if statement prepends "0" to hexvalue; unlike the threedigit conditionals, this if statement cannot be deleted without harming the script.

Finally, the selected properties of the 000 option objects of the red/green/blue selection lists are set to true.

Desiring a cross-browser loop, and recalling the use of img element strings in the random banner script of Script Tip #34, I decided to scrap the new Option( ) constructor statements and return to the Script Tips #65-67 Script's original

document.write("<option value='##'>###</option>")

approach to creating the Red/Green/Blue options. After further experimentation, I found that the function below will effectively substitute for the menu option string when using either MSIE or Netscape:

function threeDigitMenu( ) {
var optionarray = new Array( );
for (i = 0; i < 256; i++) {
var hexvalue = i.toString(16);
if (/^\w$/.test(hexvalue)) hexvalue = "0" + hexvalue;
var threedigit = i;
if (/^\d{2}$/.test(threedigit)) threedigit = "0" + threedigit;
if (/^\d$/.test(threedigit)) threedigit = "00" + threedigit;
optionarray[i] = "<option value='" + hexvalue + "'>" + threedigit + "</option>"; }
return optionarray; }

As shown in the "Separating HTML and JavaScript" section above, the red/green/blue select elements can call the threeDigitMenu( ) function via a

<script type="text/javascript">document.write(threeDigitMenu( ));</script>

script element; threeDigitMenu( ) returns to each select element the entire optionarray[ ] option array, which is then written to the page normally.

In the next entry, we'll move on to the Script Tips #69-72 Script, which codes a rotating series of hyperlinking headlines.


Comments: Post a Comment

<< Home

Powered by Blogger

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