reptile7's JavaScript blog
Saturday, November 21, 2009
 
Iceman and the Multicolumned Marquee
Blog Entry #163

Today we kick off a tour of HTML Goodies' "JavaScript Browser Test Scripts" collection of tutorials.

We'll begin with the third of these tutorials, "Redirect Based on Browser Type", because this tutorial presents a script almost identical to one we've analyzed previously. Specifically, "Redirect Based on Browser Type" briefly discusses the browser sniffer script authored by "Iceman" that is the subject of HTML Goodies' JavaScript Script Tips #13-15. The "Redirect Based on Browser Type" script is posted here; the Script Tips #13-15 script is located here. Actually, there is a small-but-significant difference between the final if blocks of these scripts, which we'll comment on later.

We covered the Script Tips #13-15 script in Blog Entry #57; that was about three years ago, and I don't really have anything to add to the deconstruction in that entry. My fundamental criticism of the script still stands, namely, if you're going to send most MSIE users to a common msiepage.html page and most Netscape users to a common nspage.html page, then there's no point whatsoever in flagging different MSIE/Netscape versions via their navigator.appVersion and navigator.userAgent returns. However, Joe intimates here that the common routing was his doing, and I have no idea what Iceman himself wanted to do with his various browser version tests.

Written in 1997, Iceman's script can be viewed as a snapshot of sorts of the chaotic state of Web standards in the late 1990s. Perhaps there really was a need to distinguish between MSIE 4.0b1, MSIE 3.02, MSIE 4.0b2, etc. back in the confusing old days, before the advent of HTML 4 brought a measure of stability to the coding of Web pages. Thankfully, the standardization of modern browsers - and a now-widespread recognition on the part of Web authors that the use of proprietary code should in general be avoided - make browser sniffing much less important than it used to be.

In practice

Joe didn't provide a demo for Script Tips #13-15 but does so for the "Redirect Based on Browser Type" tutorial. Joe puts Iceman's script in the source of this page, which accordingly directs would-be MSIE users to a "Browser Page - Explorer" page and would-be Netscape users to a "Browser Page - Netscape" page. The usage share of the browsers flagged by the first seven if statements in Iceman's script should be negligible; fortunately, the script's last two if statements allow the demo to work with modern browsers:

var r;
var browser = navigator.appName;

if (browser == "Netscape") {
// If browser is Netscape and Supports JavaScript go to Netscape Page
parent.location.href = "nspage.html";
r = 1; }

if (r != 1 && r != 2) {
/* If browser is not Nestcape or MSIE known versions go to MSIE page - caters for any other JavaScript Browsers */
parent.location.href = "msiepage.html"; }


The navigator.appName return for both Firefox and Safari is Netscape - check out the "Additional browsers' Navigator Information" section of JavaScript Kit's navigator object page - and therefore users of these browsers will be sent to the "Browser Page - Netscape" page by the script's eighth if block.

In turn, MSIE 5+ and Opera users are sent to the "Browser Page - Explorer" page by the script's ninth and last if block. These users would be sent to a "text-based" textpage.html page by the corresponding if block in the Script Tips #13-15 version of Iceman's script. (An aside: contra what Script Tip #14 suggests, Lynx users would not be redirected to the textpage.html page or anywhere else by the script because the Lynx browser does not support JavaScript.)

Now, what do we have on the "Browser Page - Explorer" and "Browser Page - Netscape" pages? Here's where things get a bit more interesting.

The marquee element

Joe festoons the top of the "Browser Page - Explorer" page with a marquee element. First implemented by Microsoft in MSIE 4 - the MSDN Library's current marquee element/object page is here - the marquee element moves its content in a scroll-like manner across the viewport, for example:

"No one can make you feel inferior without your consent." - Eleanor Roosevelt

Layout-wise, the marquee element is like the div element in that it's a block-level element, it has an effective width of 100%, and it has a %flow; content model, i.e., it can contain both block-level and inline children - it's not just a textual element. The marquee element moves its content right to left by default; via its direction and behavior attributes, it can alternatively move its content left to right, up to down, down to up, or even back and forth:

[A smilie face marquee that moves back and forth]

Diagonal motion is possible via the nesting of marquee elements:

Hello world!

<marquee direction="right"><marquee direction="up">Hello world!</marquee></marquee>

The marquee element loops its content infinitely by default, although it has a loop attribute for setting a finite number of content loops; the looping speed can be increased/decreased via scrollamount and scrolldelay attributes. The marquee element also has attributes that style it in more conventional ways: there's a bgcolor attribute that will give it a background color, width and height attributes for respectively setting its width and height, and hspace and vspace attributes for respectively setting horizontal and vertical margins around the marquee box.

At the time that "Redirect Based on Browser Type" was written, the marquee element was indeed an Explorer-only deal, but this is no longer the case. At the bottom of the "Browser Page - Explorer" page, commenter Ron correctly notes, Need to update the info on this page. Firefox has no problem with the Marquee command. And it's not just Firefox: with the exception of Lynx 2.8.7, all of the OS X browsers on my computer - Firefox plus Safari*, Opera, Camino, and of course MSIE - support the marquee element (*Safari does not support the behavior="alternate" setting for the second demo above). Moreover, I can confirm, from testing in the SheepShaver environment, that Netscape itself began support for the marquee element with Netscape 7.x (Netscape 6.x doesn't support it).

HTML Goodies and the marquee element

The first paragraph of the "Browser Page - Netscape" page contains a link to a "So, You Want An Explorer Scrolling Marquee, Huh?" tutorial, which is problematic in several respects although this is not the time/place for sorting it out, but I at least want to point out that the current "Explorer Scrolling Marquee" page is missing a demo - the hilariously funny joke in text form at the top of the screen - that can be seen on this archived "Explorer Scrolling Marquee" page.

The W3C and the marquee element

Wikipedia has an entry on the marquee element whose introduction in part states, [The marquee element] is deprecated by the W3C and not advised by them for use in any HTML documents - this is not quite accurate. The HTML 5 Specification, which is currently at the "Working Draft" stage, has an "Obsolete features" chapter with a subsection devoted to the marquee element and a "Non-conforming features" section declaring that the marquee element is entirely obsolete, and must not be used by authors...[u]se CSS instead. The marquee element does not appear in any of the W3C's earlier HTML specifications; HTML 4.01's list of currently deprecated HTML elements is here.

Because the marquee effect is a presentational effect, can be applied to a wide variety of renderable elements, and is widely supported, it makes sense that the W3C would bring it into CSS, and Wikipedia's "Marquee tag" page concludes with a link to the "marquee properties" section in CSS level 3's Basic Box Model module. Checking the W3C's "CSS Current Work" page, however, I see that the W3C is addressing the marquee effect more directly via a CSS Marquee Module Level 3 specification, which is at present "high priority" and at the "Candidate Recommendation" stage.

The multicol element

Joe marks up the first paragraph of the "Browser Page - Netscape" page with a multicol element. Implemented by Netscape in Navigator 3 - the multicol section in Netscape's HTML Guide for Netscape Navigator 4.x is here - the multicol element flows its content into columns of equal width. To the best of my knowledge, the multicol element is only supported by Navigator 3.x-4.x - like the layer element, the multicol element was jettisoned by Netscape for Netscape 6 - and therefore I am not inclined to discuss it. But if your browser did support the multicol element, the "Browser Page - Netscape" first paragraph would look more or less like this:

[A multicol paragraph]

So, how might we create a cross-browser multicolumn display today, huh? My guess is that Mozilla would say, "Go use positioned divs for that," and that's probably what you should do for the time being. However, the aforecited "CSS Current Work" page reveals that the W3C is thrashing out a Multi-column Layout module in this regard.

I have to confess that I was unfamiliar with the marquee and multicol elements prior to working through the "Redirect Based on Browser Type" tutorial - you learn something new every day! Anyway, we'll look at another approach to dealing with proprietary code in the next post when we go through the first "JavaScript Browser Test Scripts" tutorial, "Internal Browser Test".

reptile7

Wednesday, November 11, 2009
 
RAM ? Clock : Freeze
Blog Entry #162

You may recall that in the course of going through the HTML Goodies JavaScript Script Tips, we analyzed several JavaScript clock scripts:
(1) In Script Tips #25-28, we discussed a script that codes a basic, text-based digital clock.
(2) In Script Tips #84-86, we deconstructed a script that codes an image-based digital clock.
(3) In Script Tips #87-90, we dissected a script coding a "world clock" that could display times for various cities around the world.
The "So, You Want A JavaScript Clock, Huh?" tutorial in HTML Goodies' Beyond HTML : JavaScript sector presents yet another clock script, which is as well written as (if not more so than) any of the Script Tips clock scripts and which we'll briefly check over in today's post.

Demo #1, and giving credit

The "So, You Want A JavaScript Clock, Huh?" tutorial begins, A clock on your home page? Why, yes! I'll have one too, please. The thing at the top center of the page is what you can get here, i.e., the tutorial page should host a demo, which is nowhere to be seen. The key to the missing demo lies in the following sentences in the tutorial's "How To Do It..." section: You'll notice toward the bottom of the text page that there is a </script> command and then a small form program. Get that, too. It gives you the box that goes around the clock. The person who posted the current "So, You Want A JavaScript Clock, Huh?" page did not "get that, too" - he or she put the script that codes the clock in the tutorial source, but did not bring along the document body HTML that houses the clock. So I guess it's up to me to give you a demo.

At the tone the time will be:


Via the Internet Archive, I was able to track down an earlier version of the "So, You Want A Java[Script] Clock, Huh?" page (ah, back in the good ol' days, when Joe himself was at the helm of his ship). Unlike the current tutorial page, the older tutorial page
(a) sports a functioning demo,
(b) discusses in a "First Things First" subsection the 'system requirements' for the tutorial script (not so important in this day and age), and
(c) gives credit to the script's author: My special thanks to Mr. Madison Bryan for providing me with this script. You're a gentleman. Let me second Joe's gratitude: wherever you are, Madison, we thank you for your efforts.

Script deconstruction

The "So, You Want A JavaScript Clock, Huh?" script is accessed by following the tutorial's Click here to get it link. Let's begin our analysis with a look at the script's document body HTML:

<body onload="startclock( );">
<!----------------------------------------------------------------------------->
<form name="clock" onsubmit="0" action="">
<input type="text" name="face" size="11" value="....Initializing...." />
</form>


The clock display will be loaded into a text box named face held by a form named clock.

The text box's size attribute should be set to at least 13; the clock display comprises twelve or thirteen characters depending on the time of day, and thus the size="11" attribute would cut off the display's last one or two characters if the user's default font preference were set to a monospace font (e.g., Courier).

The clock form's onsubmit="0" attribute serves no purpose and can be removed. Moreover, the name attribute of the form element was deprecated by XHTML 1.0. But only the earliest browsers need a form container for the input element anyway; you might prefer to ditch the form element, give the input element an id="clockbox" attribute, and then access the input element via a document.getElementById("clockbox") reference. (N.B. Removing the form element will change the clock's 'formatting flow' from block-level to inline.)

For that matter, an <input type="text"> is semantically an odd container for the clock display given that no user 'input' is involved. A more state-of-the-art way to render the clock would be to put it in a div or span element via the DOM innerHTML property, e.g.:

<!-- To give the clock a block-level rendering per the original script, put it in a -->
<div id="clockdiv"></div>
// via
document.getElementById("clockdiv").innerHTML = timeValue;


As of this writing, innerHTML is a widely-supported but nonstandard property; however, the W3C apparently plans to 'bring it on board' for HTML 5. Alternatively, the not-quite-as-widely-supported* but standard DOM textContent property can likewise be used to load the clock into the textual element of your choice. (*On my computer, textContent is supported by Firefox, Safari, and Opera, but not by MSIE 5.2.3.)

The <!----------> comment line between the body and form element start-tags is there to improve the readability of the code, but is otherwise unnecessary and can be removed.

We're ready to get the clock under way. When the script document has finished loading in the browser window, the body element's onload event handler calls the startclock( ) function in the script element in the document head:
function startclock( ) {
    stopclock( );
    showtime( );
}
The startclock( ) function first calls the stopclock( ) function that precedes it in the script element:
var timerID = null;
var timerRunning = false;

function stopclock( ) {
    if (timerRunning)
        window.clearTimeout(timerID);
    timerRunning = false;
}
As the script stands, the stopclock( ) code is unnecessary and can be thrown out - the script will later set the Boolean timerRunning variable to true, but we won't be calling stopclock( ) again. But let's suppose we want to give the user the ability to stop the clock once it gets going via clicking a button:

<button type="button" onclick="stopclock( );">Stop the clock, now</button>

In this case, stopclock( )'s window.clearTimeout(timerID); command will effectively stop the clock by pulling the plug on the recursive function call that moves the clock along (vide infra).

startclock( ) then calls the showtime( ) function that follows it in the script element. The showtime( ) function initially creates a new Date object and then gets the hour, minute, and second parts of that object:
function showtime( ) {
    var now = new Date( );
    var hours = now.getHours( );
    var minutes = now.getMinutes( );
    var seconds = now.getSeconds( );
Mozilla's current Date object page, which links to separate pages for the getHours( ), getMinutes( ), and getSeconds( ) methods, is here.

We now build the clock display, which is given the identifier timeValue, from our Date object data. Data type-wise, timeValue will be a string containing numerals delimited by colons plus an A.M./P.M. part. As for any digital time display, timeValue begins with an hour value, which is installed by the next showtime( ) command:

var timeValue = "" + ((hours > 12) ? hours - 12 : hours);

Command comments
• We first discussed the ?: conditional operator in Blog Entry #101, which deconstructs the Script Tips #84-86 clock script.
• An empty string is prepended to the hour value in order to stringify it, i.e., to convert the hour value from a number data type to a string data type; this is unnecessary because the following showtime( ) command will append a string to (and thus stringify) the timeValue value.
• Like the Script Tips #25-28 and #84-86 clock scripts, the "So, You Want A JavaScript Clock, Huh?" script codes a 12-hour and not a 24-hour clock. Accordingly, 12 is subtracted from hours (now.getHours( )) values in the range 13-23, inclusive, i.e., for the hours spanning 1PM to midnight.
• For the midnight-to-1AM hour, the clock's hour value will be 0; if you would rather it be 12, then you should place an if (hours == 0) timeValue = 12; statement immediately after the above command.
• I was originally going to point out that both sets of parentheses are unnecessary (for reasons relating to JavaScript operator precedence and type conversion), but the parentheses do make the command more understandable, so never mind.

The next two showtime( ) commands respectively set the minute and second parts of the clock:

timeValue += ((minutes < 10) ? ":0" : ":") + minutes;
timeValue += ((seconds < 10) ? ":0" : ":") + seconds;


Other than their different variable names, these assignments are identical to the corresponding assignments in the Script Tips #87-90 clock script, which were detailed in (near the end of) Blog Entry #104.

Finally, we (a) complete the display by appending P.M. or A.M., depending on the hours value, to the timeValue string:

timeValue += (hours >= 12) ? " P.M." : " A.M.";

(b) load the display into the face text box:

document.clock.face.value = timeValue;

(c) provide a recursive function call for updating the clock:

timerID = window.setTimeout("showtime( );", 1000);
// The clock is updated every 1000 milliseconds.


and (d) set timerRunning to true:

timerRunning = true; }

Whether we do or don't keep the stopclock( ) function, neither the stopclock( ) function nor the showtime( ) function need be called via the intermediacy of the startclock( ) function; for its part, showtime( ) can be directly called by the body element start-tag

<body onload="showtime( );">

or by a

window.onload = showtime;

script command.

Two more practical points

(1) Something I didn't mention earlier: loading the clock into a textual element will allow you to more reliably style it per your preference.


And here's what time it is:


Here's the style rule set that I've applied to the clock:

#clockspan {
background-color: #b22222; /* firebrick */
color: #adff2f; /* green-yellow */
font-family: Courier, monospace;
font-weight: bold;
text-decoration: underline;
}


Modern browsers will apply most or all of the above style declarations to an input element. Nevertheless, the W3C warns here that [a]uthors are recommended to treat such support as experimental.

(2) The recursive showtime( ) call sets up an infinite loop without an exit condition, and I would think that allowing the clock to run indefinitely would give rise to a function stack overflow and cause the browser to hang, but I'm not sure about that.

Next up in the Beyond HTML : JavaScript sector is a "JavaScript Browser Test Scripts" set of tutorials that use browser/screen sniffing to do various things. These tutorials are of rather marginal utility, but I thought in the following entry we might begin a quick look at them, if only for 'archival purposes'.

reptile7


Powered by Blogger

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