Friday, January 11, 2008
Coloring by Elements
Blog Entry #100
We continue today our discussion of the rainbow text script of HTML Goodies' JavaScript Script Tips #81-83. We noted in the previous post that the Script Tips #81-83 Script commingles its document body HTML with some of its JavaScript, which raises a portability issue: for a separate document that, unlike the script document, has some real structure, can we apply the script and its coloring effects to specific text strings therein? Gratifyingly, with a little help from the Document Object Model (DOM), we can indeed transform the Script Tips #81-83 Script from an ad hoc generator of colored strings into a targeting color machine; in this post, we'll modify the script's ColoredText( ) function so that it adds color to text elements on the basis of
(a) id attribute value,
(b) tag name, and
(c) class attribute value.
A demo illustrating our handiwork will follow.
We'll apply our modified JavaScript to the following document body:
<body>
<h1>A sample with shades of green</h1>
<p>
<span class="rainbow">This is a text sample written using the function ColoredText.</span>
<br />
<span id="span1">Another text sample, this time only with red and orange repeatedly</span>
</p>
<h1>This is another H1 heading</h1>
<p id="p1" class="rainbow">Try refreshing the page a couple of times!</p>
</body>
<h1>A sample with shades of green</h1>
<p>
<span class="rainbow">This is a text sample written using the function ColoredText.</span>
<br />
<span id="span1">Another text sample, this time only with red and orange repeatedly</span>
</p>
<h1>This is another H1 heading</h1>
<p id="p1" class="rainbow">Try refreshing the page a couple of times!</p>
</body>
setColorsById( )
We begin with the one-off coloring of a unique element. Consider the string Another text sample, this time only with red and orange repeatedly, whose characters are alternately colored red and orange by the Script Tips #81-83 Script and which my sample document wraps in a <span id="span1"> ... </span> container. This string can now be accessed and overwritten by the ColoredText( ) function via
document.getElementById("span1").innerHTML
expressions. (As noted in Blog Entry #70, innerHTML is not a standard (W3C-approved) DOM property. The DOM Level 3 Core Specification introduces a textContent property that is similar to innerHTML and that I would use below if the browsers on my computer supported it, which they do not.)
By combining the document.getElementById("span1").innerHTML expressions with parts of the Script Tips #81-83 Script and with some statements analogous to those appearing in the "To return or not to return" section of the previous post, I came up with the following JavaScript module that successfully colors the Another text sample, this time only with red and orange repeatedly string à la the Script Tips #81-83 Script:
var redorangeArray = new Array("red", "orange");
var myString = document.getElementById("span1").innerHTML;
var redorangeString = "";
for (i = 0; i < myString.length; i++) {
var charColor = myString.charAt(i);
colorCode = i % redorangeArray.length;
tempStr = charColor.fontcolor(redorangeArray[colorCode]);
redorangeString += tempStr; }
document.getElementById("span1").innerHTML = redorangeString;
var myString = document.getElementById("span1").innerHTML;
var redorangeString = "";
for (i = 0; i < myString.length; i++) {
var charColor = myString.charAt(i);
colorCode = i % redorangeArray.length;
tempStr = charColor.fontcolor(redorangeArray[colorCode]);
redorangeString += tempStr; }
document.getElementById("span1").innerHTML = redorangeString;
The span1 string is replaced by a redorangeString string that is assembled on a character-by-character basis. The red and orange color keyword values are assigned on the first line to a redorangeArray array; their respective redorangeArray indexes, 0 and 1, match the colorCode % returns, which thus do not need to be incremented as was necessary in the else block of the original script.
setColorsByTagName( )
We set our sights higher in this section as we apply the sequence of green shades in the Script Tips #81-83 Script's third ColoredText( ) function call to both of the h1 elements in the above sample document. The A sample with shades of green and This is another H1 heading strings are smoothly colored with the #00x000 sequence via the following JavaScript module:
var headings1 = document.getElementsByTagName("h1");
var greenArray = new Array("#006000", "#007000", "#008000", "#009000", "#00a000", "#00b000");
var greenString = new Array( );
for (i = 0; i < headings1.length; i++) {
greenString[i] = "";
var myString = headings1[i].innerHTML;
for (j = 0; j < myString.length; j++) {
var charColor = myString.charAt(j);
colorCode = j % greenArray.length;
tempStr = charColor.fontcolor(greenArray[colorCode]);
greenString[i] += tempStr; }
headings1[i].innerHTML = greenString[i]; }
var greenArray = new Array("#006000", "#007000", "#008000", "#009000", "#00a000", "#00b000");
var greenString = new Array( );
for (i = 0; i < headings1.length; i++) {
greenString[i] = "";
var myString = headings1[i].innerHTML;
for (j = 0; j < myString.length; j++) {
var charColor = myString.charAt(j);
colorCode = j % greenArray.length;
tempStr = charColor.fontcolor(greenArray[colorCode]);
greenString[i] += tempStr; }
headings1[i].innerHTML = greenString[i]; }
In brief:
(1) The h1 elements are scooped up and arrayed via the DOM's getElementsByTagName( ) method.
(2) Separate arrays are set up for the
(a) #00x000 green hex codes and
(b) greened variants of the h1 element strings.
(3) A parent for loop sequentially accesses the h1 element strings and then overwrites them with their greened variants.
(4) A nested for loop creates the greened strings à la the previous section.
setColorsByClass( )
Finally, in this section we'll randomly color the text strings of the sample document's first span element and second p element, which have a common class="rainbow" identifier, with the color values of the defClrsArray array. Unfortunately, the DOM does not have a "getElementsByClass( )" method that would allow us to directly array these elements and thus color them via the "setColorsByTagName( )" approach above. Fortunately, a Google search led me to a script posted at The JavaScript Source and authored by Dustin Diaz that uses the getElementsByTagName( ) method and the DOM className property to indirectly (more circuitously) array elements with a given class attribute value. A watered-down version of Mr. Diaz's script that is suitable for our present purpose is given below:
var classElements = new Array( );
var els = document.getElementsByTagName("*");
var elsLen = els.length;
for (i = 0, j = 0; i < elsLen; i++) {
if (els[i].className == "rainbow") {
classElements[j] = els[i];
j++; } }
var els = document.getElementsByTagName("*");
var elsLen = els.length;
for (i = 0, j = 0; i < elsLen; i++) {
if (els[i].className == "rainbow") {
classElements[j] = els[i];
j++; } }
Much like a document.all expression, the document.getElementsByTagName("*") expression returns an array of all of a document's elements, which is assigned in the code above to the variable els. We then loop through the els array and use an
if (els[i].className == "rainbow") { classElements[j] = els[i]; j++; }
conditional to fish out those elements whose className value is rainbow; the class="rainbow" elements are sequentially assigned to a classElements array.
The strings contained by the classElements elements can now be accessed and colored per the previous section:
var defClrsArray = new Array("red", "purple", "cyan", "green", "blue", "magenta");
var rainbowString = new Array( );
for (i = 0; i < classElements.length; i++) {
rainbowString[i] = "";
var myString = classElements[i].innerHTML;
for (j = 0; j < myString.length; j++) {
var charColor = myString.charAt(j);
colorCode = Math.floor(Math.random( ) * defClrsArray.length);
/* For periodic coloring, replace the preceding line with: colorCode = j % defClrsArray.length; */
tempStr = charColor.fontcolor(defClrsArray[colorCode]);
rainbowString[i] += tempStr; }
classElements[i].innerHTML = rainbowString[i]; }
var rainbowString = new Array( );
for (i = 0; i < classElements.length; i++) {
rainbowString[i] = "";
var myString = classElements[i].innerHTML;
for (j = 0; j < myString.length; j++) {
var charColor = myString.charAt(j);
colorCode = Math.floor(Math.random( ) * defClrsArray.length);
/* For periodic coloring, replace the preceding line with: colorCode = j % defClrsArray.length; */
tempStr = charColor.fontcolor(defClrsArray[colorCode]);
rainbowString[i] += tempStr; }
classElements[i].innerHTML = rainbowString[i]; }
FYI: Mr. Diaz's script addresses the complication that an element can belong to more than one class, e.g.,
<p class="class1 class2 class3">This is a paragraph.</p>
<!-- The HTML class attribute is detailed here. -->
by comparing the els[i].className expression to (simplifying once again) a (^|\s)classValue(\s|$)-like regular expression.
Demo
Here's what it all looks like:
A sample with shades of green
This is a text sample written using the function ColoredText.
Another text sample, this time only with red and orange repeatedly
This is another H1 heading
Try refreshing the page a couple of times!
Relevant demo CSS:
div { background-color: #ffffcc; font-size: 16px; }
#p1 { font-family: Courier, monospace; font-size: 24px; text-align: center; }
A couple of points:
• #ffffcc is a member of the set of so-called Web-safe colors, which are discussed by Wikipedia here.
• In the Script Tips #81-83 Script, the Try refreshing the page a couple of times! string is marked up with a size="+2" attribute that should in this case be reproducible by a font-size:x-large; declaration, which in practice (without getting into the details) performed as expected with Netscape but not with MSIE on my computer, so I decided to use <length> font-size values for the div and p1 elements instead.
In the following entry, we'll look at the Script Tips #84-86 Script, which codes an image-based digital clock.
reptile7
Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)