reptile7's JavaScript blog
Thursday, June 26, 2014
 
The Text at the End of the Rainbow
Blog Entry #325

The Java Goodies: Scripts that Display Text script collection contains three scripts that color text strings on a character-by-character basis:
(1) "Color Gradient Text"
(2) "Multi-Colored Text"
(3) "Rainbow Text"
We have previously worked through the Color Gradient Text and Multi-Colored Text scripts; we'll take up the Rainbow Text script in today's post. The Rainbow Text script was authored by Ian Moore in 1996 or 1997 or 1998.

Unlike the Color Gradient Text and Multi-Colored Text scripts, the Rainbow Text script is an interactive application that allows the user to input a document range into a textarea box, click an button, and display the colored range in a new window.

Joe's demo

The script's demo basically works OK although the upper part of its interface is a bit confusing. Situated above the textarea field is an HTML Source checkbox and a selection list comprising and options.

The Rainbow Text script interface (This is a screenshot of the interface and not a working demo.)

The author wants the user to check the checkbox if the range to be inputted is an entire document that includes start-tags and end-tags for the html, head, title, and body elements; this markup is added to the user's input if the checkbox is left unchecked - or would be added, if the script's test of the checkbox's checked status were not out of order.

The selection list can be used to give the new window document a black or white background color if the checkbox is left unchecked. For his demo, Joe effectively disabled the selection list by removing the script's body bgcolor operation:

mywin.document.writeln("<body bgcolor='" + backColor + "'>");

The Rainbow Text script appears to color the characters of text strings randomly à la the Multi-Colored Text script; we'll see in due course that it actually colors them according to an arrayed sequence of colors.

Pre-display

The interface controls and the name="myform" form that contains them are coded in a conventional manner and there's no need for us to go through this HTML, so with no further ado let's get to the script's JavaScript, which begins by setting out the colors we'll be applying to the textarea value.

myColor = new Array(1);
myColor[0] = "#FF0000";
myColor[1] = "#CC0099";
myColor[2] = "#990099";
myColor[3] = "#CC00FF";
myColor[4] = "#330099";
myColor[5] = "#006699";
myColor[6] = "#33FF99";
myColor[7] = "#336600";
myColor[8] = "#666000";
myColor[9] = "#996600";
myColor[10] = "#FF9900";
myColor[11] = "#FFCC33";
myColor[12] = "#FFFF00";
myColor[13] = "#FF9966";


Excepting #666000, all of the myColor colors are Web-safe colors.

At a later point we will sequentially access the myColor values via a b variable, which is initialized to 0.

var b = 0;

Clicking the button

<input type="button" value="OK" onclick="morefun( );">

calls a morefun( ) function. The morefun( ) function first gets the value of the selection list's selected option and assigns it to a backColor variable.

function morefun( ) { var backColor = document.myform.myselect[document.myform.myselect.selectedIndex].value;
...
<form name="myform">
HTML Source <input type="checkbox" name="isHTML">
<select name="myselect">
<option value="black">Black Background
<option value="white">White Background
</select><br>


It's not necessary to use the options property of the Select object to access a <select>'s <option>s. Upon probing the document.myform.myselect object with a for...in statement

var result = ""; for (var i in document.myform.myselect) result += "document.myform.myselect" + "." + i + " = " + document.myform.myselect[i] + "<br>"; document.write(result);

the first two outputted lines are:

document.myform.myselect.0 = [object HTMLOptionElement]
document.myform.myselect.1 = [object HTMLOptionElement]


The morefun( ) function next opens a new window.

mywin = newwin = window.open("", "mywin", "toolbar=no,directories=no,menubar=yes,scrollbars=yes,resizable=no,copyhistory=no,location=no");

It's been a while since we've dealt with a window.open( ) command. Mozilla's current window.open( ) page is here. The W3C will be bringing window.open( ) into HTML5.

The new window is blank as the command's strUrl parameter is set to an empty string. FYI: For Safari users, the strUrl parameter must be set to about:blank to give the new window a <title> value (otherwise the title will be Untitled).

Oddly, the new window is given two object references: mywin and newwin. Is there any need to do this? None whatsoever - indeed, the script makes no use of the newwin identifier. The new window's name property is set to mywin.

Note that the command's strWindowFeatures string does not contain width and height settings. Re the size of the new window, here's what I see as regards the modern OS X browsers on my computer:
(F) The new window and the opener window have the same outerWidth/outerHeight dimensions when using Firefox.
(O) The new window has an outerWidth of 403 and an outerHeight of 595 regardless of the size of the opener window when using Opera.
(S) The new window and the opener window have the same innerWidth/innerHeight dimensions when using Safari.

The strWindowFeatures string turns on the menubar and scrollbars features and that's it. In practice:
• The new window is given an address bar by Firefox and Opera but not by Safari.
• The new window is resizable with Firefox, Opera, and Safari.
• There are no scrollbars if the document content does not overflow the viewport.
Other notes:
• The directories feature is obsolete: use personalbar instead.
• We previously discussed the nonstandard copyhistory feature in the More on copyhistory subsection of Blog Entry #154.

The strWindowFeatures string is much more cumbersome than it needs to be and can be shrunk to menubar,scrollbars: it's not necessary to explicitly set menubar and scrollbars to yes or turn off the other specified features.

Moving back to the opener document, the morefun( ) function gets the user's textarea input and assigns it to a mystring variable.

mystring = document.myform.myarea.value;
...
<textarea name="myarea" rows="10" cols="30"></textarea>


The remainder of the morefun( ) function assembles the new window document. An if statement writes some introductory markup and sets the body background color if the "status" of the isHTML checkbox is false.

if (document.myform.isHTML.status == false) { mywin.document.writeln("<html><head>"); mywin.document.writeln("<title>Somewhere over the rainbow....</title></head>"); mywin.document.writeln("<body bgcolor='" + backColor + "'>"); }

The Checkbox object does not in fact have a status property (the only classical JavaScript object having a status property is the window object): the author meant to check the checkbox's checked property. The document.myform.isHTML.status expression evaluates to undefined and the undefined == false comparison returns false ⇒ nothing is written regardless of whether the checkbox is checked or unchecked. However, if we change status to checked and leave the checkbox unchecked, then the if body is executed (and would overwrite the corresponding <html>/<head>/<title>/<body> markup in the user's input if it were present).

Is there any reason to use the writeln( ) method here as opposed to the write( ) method? No.

If you're going to run this code through a validator, don't forget to escape the </ character sequences in the end-tags:

mywin.document.write("<title>Somewhere over the rainbow....<\/title><\/head>");

The body background color can alternatively be set with a style element vis-à-vis the deprecated bgcolor attribute:

mywin.document.write("<style type='text/css'>body { background-color: " + backColor + "; }<\/style>");

We'll continue our deconstruction of the Rainbow Text script in the following entry.

Thursday, June 19, 2014
 
A String Movie
Blog Entry #324

In today's post we'll discuss the Scripts that Display Text collection's "Animated Text on Status Bar" (AToSB) script, which was authored by Bart Jellema. The AToSB script codes an animation that appears to continuously move rightwards and leftwards double arrows into a central string. The AToSB animation is carried out by running separate sets of strings in and out of the beginning and ending of a window.status string, and is analogous to animations that cycle a set of images in and out of a common <img> placeholder (e.g., see the script offered by HTML Goodies' JavaScript Primers #28).

The AToSB script is posted here and its demo page is here. The script output is easily moved from the status bar to an HTML container that can be styled. Click the button below for a span-based demo that enlarges and adds some color to the original display.

What do you think of this?



Script deconstruction

The script begins by initializing five variables.

<script language="javascript">
var hellotext = "What do you think of this?";
var thetext = "";
var started = false;
var step = 0;
var times = 1;


Loading the page

<body bgcolor="white" onload="welcometext( );">

calls a welcometext( ) function.

function welcometext( ) { times--; if (times == 0) { if (started == false) { started = true; window.status = hellotext; window.setTimeout("anim( );", 1); } thetext = hellotext; } }

The welcometext( ) function decrements times to 0, switches started to true, writes the hellotext string to the status bar, triggers an anim( ) function, and copies the hellotext string to thetext. BTW, the minimum window.setTimeout( ) delay is 4 milliseconds.

The anim( ) function assembles the 'frames' of the AToSB animation and sequentially loads them into the status bar.

function anim( ) { step++; if (step == 7) { step = 1; } if (step == 1) { window.status = ">===" + thetext + "===<"; } if (step == 2) { window.status = "=>==" + thetext + "==<="; } if (step == 3) { window.status = ">=>=" + thetext + "=<=<"; } if (step == 4) { window.status = "=>=>" + thetext + "<=<="; } if (step == 5) { window.status = "==>=" + thetext + "=<=="; } if (step == 6) { window.status = "===>" + thetext + "<==="; } window.setTimeout("anim( );", 200); }

There are three parts to each frame, from left to right:
(1) a four-character string that mimics one or two (rightwards double arrow) characters by combining one or two > characters and three or two = characters;
(2) the thetext string; and
(3) a four-character string that mimics one or two (leftwards double arrow) characters by combining one or two < characters and three or two = characters.

The frames are effectively arrayed by a step counter; note that
(a) the > characters shift from left to right and
(b) the < characters shift from right to left
as step increases from 1 to 6.

The anim( ) function calls itself every 200 milliseconds. As it iterates, anim( ) increments step and uses it to order the animation process. When step hits 7 it is reset to 1 and the frame sequence begins anew.

In the name of completeness

Between the welcometext( ) function and the anim( ) function is a showstatustext( ) function

function showstatustext(txt) { thetext = txt; window.setTimeout("welcometext( );", 4000); times++; }

that is never called and can be thrown out.

Prune it

Actually, the showstatustext( ) function isn't the only dispensable part of the AToSB script; the hellotext, started, and times variables and the welcometext( ) function are all excess baggage. Moreover, the anim( ) function can itself be substantially slimmed down.

Here is the coding approach I used in crafting the demo at the outset of the post:

(1) Initialize step to 0 as in the original script; assign the central string to thetext from the get-go; put the changing parts of the display in bona fide arrays.

var step = 0;
var thetext = "What do you think of this?";
var leftArray = [">===", "=>==", ">=>=", "=>=>", "==>=", "===>"];
var rightArray = ["===<", "==<=", "=<=<", "<=<=", "=<==", "<==="];


(2) Cue up an animSpan span container to hold the display. Get the animSpan span and set the anim( ) function in motion when the page loads.

var animSpan, animID;
window.onload = function ( ) { animSpan = document.getElementById("animSpan"); animID = window.setInterval(anim, 200); }
...
<span id="animSpan"></span>


(3) Recast the anim( ) function as:

function anim( ) { animSpan.textContent = leftArray[step] + thetext + rightArray[step]; step = (step < 5) ? (step + 1) : 0; }

See? No need for all those if statements once step-synchronized leftArray and rightArray arrays are in place.

"What about the color?"

Oh yeah - I did that with:

animSpan.innerHTML = leftArray[step].fontcolor("red") + thetext + rightArray[step].fontcolor("blue");

Thursday, June 12, 2014
 
Hour of the Blink
Blog Entry #323

Toward the end of the Scripts that Display Text collection there's another blink-themed script, "Blinking Greetings", so why don't we do that one today? Per its title, the Blinking Greetings script displays to the user a blinking greeting, more specifically, it
(a) loads a time-appropriate string into a blink element,
(b) appends to the <blink>ing string a non-blinking " and WELCOME to xyz HomePage!!" string,
(c) formats (<center>s, sizes, italicizes, and bolds) the resulting string, and
(d) writes the formatted string to the page.
The Blinking Greetings script was authored by Effie Ram at some point during the 1996-1998 period.

The script's demo page is here - you'll see the greeting but its time-appropriate part won't blink at you unless you're using an older version of Firefox or Opera - and the script itself is posted here.

Greeting assembly

The Blinking Greetings script comprises a single script element that commingles structure, presentation, and behavior:

<script language="javascript"><!--
var mess1 = "";
var outmess = " and WELCOME to xyz HomePage!!";
document.write("<center><font size='+1'><i><b>");
day = new Date( );
hr = day.getHours( );
if ((hr >= 0) && (hr <= 4)) mess1 = "Good Night...";
if ((hr >= 4) && (hr < 12)) mess1 = "Good Morning";
if ((hr >= 12) && (hr <= 17)) mess1 = "Good Afternoon";
if ((hr >= 17) && (hr <= 23)) mess1 = "Good Evening";
document.write("<blink>");
document.write(mess1);
document.write("</blink>");
document.write(outmess);
document.write("</b></i></font></center>");
//---></script>


The script first initializes a mess1 variable to an empty string and an outmess variable to the aforementioned  and WELCOME to xyz HomePage!! string. Subsequently the script gets the current Date( ) and looks at its getHours( ) return (hr).
• If hr is in the range 0 to 4 (inclusive) - time-wise this would be 00:00:00 to 04:59:59.999 - then the first if conditional sets mess1 to Good Night....
• If hr is in the range 4 (inclusive) to 12 (exclusive) - 04:00:00 to 11:59:59.999, time-wise - then the second if conditional sets mess1 to Good Morning.
• If hr is in the range 12 to 17 (inclusive) - 12:00:00 to 17:59:59.999, time-wise - then the third if conditional sets mess1 to Good Afternoon.
• If hr is in the range 17 to 23 (inclusive) - 17:00:00 to 23:59:59.999, time-wise - then the fourth if conditional sets mess1 to Good Evening.

The mess1 text is wrapped in a blink element, the blink element is followed by the outmess text, and the <blink>mess1</blink>outmess message is marked up and outputted as detailed above. Sehr einfach, ja?

Ciao, x

Structurally, it is simple enough to put the mess1 text in a span and the <span>mess1</span>outmess content in a div or span. Presentationally, it is simple enough to move the <center>/<font>/<i>/<b> formatting to a style sheet. But what about the blinking part? The browsers that do not support the blink element - IE, Google Chrome, Safari, recent versions of Firefox and Opera - similarly do not support the blink element's stringObject.blink( ) JavaScript equivalent and text-decoration:blink; CSS equivalent. If you are determined to foist some blinking text on one and all, what are you gonna do?

Fortuitously, the Implementation section of Wikipedia's "Blink element" entry sports a JavaScript snippet that's just what we're looking for. The snippet's key statement is:

s.style.visibility = (s.style.visibility === "visible") ? "hidden" : "visible";

The s variable references a 'blink object', in this case an object representing a <blink>Text to blink here</blink> element. The statement's ?: condition reads s's CSS visibility and tests if it's visible:
(1) if so, then s's visibility is turned off by setting it to hidden;
(2) if not, then s's visibility is turned on by setting it to visible.
The s object blinks if its visibility is repeatedly toggled in this way - this is pretty close to what we were doing with the Proclaim It! script in the previous post, you may recall. BTW, there's no need to use the === strict equality operator in the ?: condition: the == operator will do.

If s's visibility is not set via an inline style attribute

<blink style="visibility:visible;">Text to blink here</blink>

or JavaScriptically

s.style.visibility = "visible";
... visibility-testing code ...


then s.style.visibility will evaluate to an empty string the very first time we read it: in this case the ?: condition returns false (even though s is visible) and visible is (re-)assigned to s.style.visibility so that s can be invisibilized next time.

The blinkee

The Wikipedia script will blink any renderable content - text, an image, a form, a table, whatever - whereas the original blink element only blinks text. (I'd have more to say about the blink element's content model if the blink element were not 'on the way out'.)

A blinks array

The Wikipedia script gets the <blink>Text to blink here</blink> element via a var blinks = document.getElementsByTagName("blink"); operation and is set up to handle any number of blink elements. IMO, however, one blinking element is controversial enough: don't overdo it.

An alternative

The code presented in the Losing weight section of the previous post can also be used to blink a single string or other unit of content: just set string2 to an empty string and you're good to go.

I'll wait

The visibility-testing code is part of a recursive blink( ) function that calls itself every 1000 milliseconds and is initially triggered by a DOMContentLoaded event or a load event:

if (document.addEventListener) document.addEventListener("DOMContentLoaded", blink, false);
else if (window.addEventListener) window.addEventListener("load", blink, false);
else if (window.attachEvent) window.attachEvent("onload", blink);
else window.onload = blink;


I was unfamiliar with the DOMContentLoaded event so I looked it up: it fires when a document has been completely loaded and parsed, without waiting for style sheets, images, and subframes to finish loading, quoting Mozilla. Are we really in that much of a rush to get the blinking action under way? I'm not. If we don't have any other functions 'listening' for a load event, then a

window.onload = function ( ) { blinkID = window.setInterval(blink, 1000); }

trigger is good enough for our purposes, wouldn't you say?

What time of day was it again?

The demo below blends the original Blinking Greetings script, the Wikipedia script, and my Losing weight code. Click the button to replace the beginning ellipsis with a blinking, time-appropriate mess1 string. To stop the blinking, click the button.

... and WELCOME to xyz HomePage!!


Thursday, June 05, 2014
 
Give Us a Blink
Blog Entry #322

In today's post we'll go through the "Proclaim It!" script that, with respect to the Scripts that Display Text timeline, precedes the "Flashing Warning" script discussed in the previous post. Like its successor, the Proclaim It! script works with the layer element/object and was authored by Greg Bland in 1998.

You may be familiar with the blink( ) method of the String object, which is poorly supported and whose <blink> HTML reflection will be obsoleted by HTML5. The Proclaim It! script slowly blinks ('proclaims') two alternating text strings at the user. To see the script's effect, click the button below. To stop the blinking, click the button.

Insanity is doing the same thing over and over again...



In the original script (as opposed to my demo, which does not make use of layers), the blinking is achieved not via the blink( ) method but by manipulating the visibility of layers containing the text strings.

The Proclaim It! script's demo page is here - you'll see Greg's two text strings but they won't blink at you unless you're using Netscape 4.x. The script itself is posted here.

Layers, fonts, and centers

The alternating text strings are housed in separate layer elements that, via their left and top attributes, have the same position - i.e., the second-in-source-order layer is stacked on top of the first-in-source-order layer - more specifically, they are both anchored to the origin of the document body. Each text string is sized and colored by a font element and is horizontally centered in the document body by a center element. The display is separated from the rest of the page by a spacer element. Do I really need to put this HTML in front of you? Oh, why not:

<layer name="humble" left="0" top="0" bgcolor="ffffff" visibilty="show">
<font size="7" font color="800080"><center>
It is hard to be humble...........
</font></center></layer>
<layer name="dabomb" left="0" top="0" bgcolor="ffffff" visibility="hide">
<font size="7" font color="800080"><center>
When You are Da BoMB.
</font></center></layer>
<spacer type="vertical" size="60">


The preceding code contains several defects that in practice don't cause any problems when I run the script with Netscape Communicator 4.61 in the SheepShaver environment.

• The visibilty attribute name in the start-tag of the humble <layer> is misspelled - not a big deal in this case as the browser ignores the visibilty attribute, the humble layer inherits the visibility of its <body> parent, and the It is hard to be humble........... string is written out per the author's intention.

• A stray font keyword appears in each font element start-tag; the extra fonts are also ignored by the browser.

• The font and center elements are improperly nested.

• The layer and font bgcolor values should be prefaced by # marks.

Not that we want to be holding onto this markup, of course: we'll exchange it for a single <div> at the end of the post.

The visibility cycle

The blinking action is carried out by a recursive chng( ) function and is directed by the value of an x variable.

<script>
x = 1;

function chng( ) { if (x == 1) { document.humble.visibility = "hide"; document.dabomb.visibility = "show"; x = 3; } if (x == 2) { document.humble.visibility = "show"; document.dabomb.visibility = "hide"; x = 4; } if (x == 4) { x = 1; } if (x == 3) { x = 2; } window.setTimeout("chng( );", 4000); }
</script>
<body onload="chng( );" bgcolor="ffffff">


The script initializes x to 1 before the page loads and calls chng( ) when the page has loaded. The if (x == 1) conditional immediately turns off the humble layer by setting its visibility to hide and turns on the dabomb layer by setting its visibility to show and then sets x to 3. Subsequently the if (x == 3) conditional adjusts x to 2.

Four seconds later chng( ) is re-called. The if (x == 2) conditional turns on the humble layer, turns off the dabomb layer, and sets x to 4. The if (x == 4) conditional resets x to 1, chng( ) is re-called after another four seconds, and so on.

Losing weight

The Proclaim It! script works well as far as it goes but does contain some excess baggage that we ought to clear out. Why use two layer elements when we can cycle the two text strings in and out of the same container? Why use separate conditionals to display the strings? What's with the 3 and 4 x values? Here's how we may deal with these issues and otherwise update the script:

body { background-color: white; }
#proclaimDiv { background-color: white; color: purple; font-size: 48px; text-align: center; }
...
var x = 1;
var string1 = "Insanity is doing the same thing over and over again...";
var string2 = "...and expecting a different result.";

function chng( ) { document.getElementById("proclaimDiv").textContent = (x == 1) ? string2 : string1; x = (x == 1) ? 2 : 1; }
window.onload = function ( ) { window.setInterval(chng, 2000); }
...
<body>
<div id="proclaimDiv">Insanity is doing the same thing over and over again...</div>


The center element is a type of div element, so you should replace the humble layer with a div if you want to hold onto the block-level, width=100% rendering of the original display; alternatively, if you want an inline display then you should use a span to house the string1 string (as I do for my demo).

We know from prior work that a size="7" font element attribute gives a 48px font-size.

The related chng( ) conditionals are easily merged via the ?: conditional operator, which is documented here in the Mozilla JavaScript Reference. The parentheses surrounding the ?: conditions are there for the purpose of readability, and can be removed if desired as the == equality operator takes precedence over the ?: operator.

My string1/string2 strings are #PCDATA text, so I load them into the proclaimDiv div via the W3C-valid textContent attribute of the Core DOM's Node interface, which is detailed by Mozilla here; if the strings contained any markup I'd use the nonstandard innerHTML property for this purpose.

Per my preference, I use a window.setInterval( ) command vis-à-vis window.setTimeout( ) to (re-)call the chng( ) function; I recommend a 2000-millisecond delay if a 4000-millisecond delay is too long for your taste. Regarding the very first string1string2 string transition, note that my code keeps the string1 string around for two seconds, whereas the original code immediately replaces the string1 string with the string2 string, as noted earlier.

Lastly and least, you can put a 60px vertical spacer between the proclaimDiv div and whatever comes after it by giving the latter a position:relative;top:60px; styling.


Powered by Blogger

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