reptile7's JavaScript blog
Monday, November 26, 2012
Snow Windows
Blog Entry #271

We continue today our analysis of Altan d.o.o.'s falling snow scripts. Altan's original snow script was written in 1999; Altan updated his script in 2005, at a time when the current versions of Internet Explorer and Netscape were IE 6 and Netscape 7, respectively. Altan's updated script accordingly throws out the original script's layer code and uses a (not-quite-)cross-browser snowIE_NS6( ) function to reposition and move the snowflakes.

function snowIE_NS6( ) { ...     for (i = 0; i < no; ++i) { ...         document.getElementById("dot" + i) = yp[i] + "px";         document.getElementById("dot" + i).style.left = xp[i] + am[i] * Math.sin(dx[i]) + "px"; }     snowtimer = window.setTimeout("snowIE_NS6( );", 50); }

The Core DOM's document.getElementById( ) method, CSS 2's top and left properties...ah, now we're getting somewhere. The updated script repeats the original script's cumbersome snowflake div creation code, but we know how to deal with that.

The updated script has two new configuration options:
(1) A hidesnowtime variable can be set to a number that will stop the snowing and hide the snowflakes after number seconds.
(2) A snowdistance variable can be set to a pageheight string that causes the snowflakes to fall the entire height of the page (document content) for pages whose heights exceed the viewport height.
We will discuss these options and other differences between the updated and original scripts in this and the next posts.

More gatekeeperism

Not only does the updated script lose the layer code but it also uses an ns6up flag to lock out pre-6 versions of Netscape from some of its sections.

var ns6up = (document.getElementById && !document.all) ? 1 : 0;
/* Netscape and IE support for document.getElementById( ) began with Netscape 6 and IE 5, respectively. */

The updated script retains the original script's ie4up gatekeeper as it contains a small amount of code that was (not-quite-)IE only at the time the script was written.

var ie4up = (document.all) ? 1 : 0;

• The ie4up flag returns 1 for Opera 5-9.2; Opera 8 was the current version of Opera when the script was written.
Google Chrome wasn't around in 2005 but some early versions of Safari were; the latter were almost certainly green-lighted by the ns6up flag.
• As for modern browsers, the ie4up flag only returns 1 for IE whereas the ns6up flag returns 1 for the rest of the gang - Mozilla's browsers, Google Chrome, Safari, and Opera 9.5+ do support document.all but they "cloak" that support via returning false for document.all in a boolean context.

Of course, it would be preferable to not have to use the ie4up/ns6up flags in the first place, but it is not possible to discard them (and the proprietary code they guard) without leaving someone behind - more on this below.

Snow, snow, go away

As shown above, a setTimeout( )-mediated recursive call to the snowIE_NS6( ) function effects the script's snow animation.

snowtimer = window.setTimeout("snowIE_NS6( );", 50);

The snowIE_NS6( ) function is followed in the source by a hidesnow( ) function that clears the snowtimer timeout and hides each snowflake by setting its CSS visibility property to hidden.

function hidesnow( ) {
    if (snowtimer) window.clearTimeout(snowtimer);
    for (i = 0; i < no; i++) document.getElementById("dot" + i).style.visibility = "hidden"; }

If the value of the aforementioned hidesnowtime configuration variable is greater than 0, then a call to the hidesnow( ) function is scheduled just after the initial call to the snowIE_NS6( ) function.

// Configure whether snow should disappear after x seconds (0=never): var hidesnowtime = 7;
if (ie4up || ns6up) {
    snowIE_NS6( );
    if (hidesnowtime > 0) window.setTimeout("hidesnow( );", hidesnowtime * 1000); }

My minor cosmetic changes notwithstanding, the script's code for stopping and hiding the snow can be left alone, but we may need to get out some Mr. Clean for the next section...

Viewport dimensions, take 2

In demarcating the snowing field of action, the script first sets a snowdistance variable to a pageheight or windowheight string.

// Configure how much snow should drop down before fading ("windowheight" or "pageheight")
var snowdistance = "pageheight";

Next, the script reads the viewport width and height.

function iecompattest( ) {
    return (document.compatMode && document.compatMode != "BackCompat") ? document.documentElement : document.body; }

if (ns6up) {
    doc_width = self.innerWidth;
    doc_height = self.innerHeight; }
else if (ie4up) {
    doc_width = iecompattest( ).clientWidth;
    doc_height = iecompattest( ).clientHeight; }

For browsers that go through the ns6up gate, the window object's innerWidth and innerHeight are respectively assigned to doc_width and doc_height variables, as in the original script. (You may know that the innerWidth return includes the width of a vertical scrollbar if present and the innerHeight return includes the height of a horizontal scrollbar if present, but this is not an issue because these returns are adjusted to prevent any snowflakes from being repositioned outside the viewport.)

For browsers that go through the ie4up gate the situation is more complicated. The iecompattest( ).clientWidth and iecompattest( ).clientHeight expressions in the else if block call an iecompattest( ) function that tests (a) if the browser supports the compatMode property of the document object AND (b) if the document.compatMode return is not equal to the string BackCompat: if the browser is running in strict mode, for which document.compatMode returns CSS1Compat, then both tests pass and document.documentElement, a reference to the document's root/html element, is returned to the iecompattest( ) calls; if the browser is running in quirks mode, then document.body, a reference to the document's body element, is returned to the iecompattest( ) calls. Finally, document.documentElement.clientWidth or document.body.clientWidth is assigned to doc_width and document.documentElement.clientHeight or document.body.clientHeight is assigned to doc_height.

According to HowToCreate's "Window size and scrolling" tutorial, Microsoft has for IE 6+ designated document.documentElement.clientWidth/document.documentElement.clientHeight for returning the viewport dimensions in strict mode and document.body.clientWidth/document.body.clientHeight for returning the viewport dimensions in quirks mode. (As a Mac user I am unable to confirm this.) Recall that the corresponding else if block in the original script, which was written when IE 5 was the current version of IE, simply assigns document.body.clientWidth and document.body.clientHeight to doc_width and doc_height, respectively. IE 5 doesn't have a strict mode; indeed, according to Quirksmode's "Quirks mode and strict mode" tutorial (see The differences section) IE 5.5 effectively defines quirks mode for IE.

For the great majority of users,

doc_width = document.body.clientWidth;
doc_height = document.body.clientHeight;

will get the viewport dimensions when in quirks mode, and this is what I used to measure the viewport in my original (2012) demos for the original script. However, HowToCreate notes that with some browsers - Konqueror, some versions of Safari and iCab* - document.body.clientWidth and document.body.clientHeight return the page dimensions. HowToCreate would advise us to run as many browsers as possible through the innerWidth/innerHeight statements and then use clientWidth/clientHeight statements to accommodate IE 5-8 users, so maybe we should leave Altan's viewport-measuring code alone too.
(*Sure enough, my original script demos do not work with iCab 3.0.5 in the SheepShaver environment.)

HowToCreate claims that IE 9+ supports innerWidth/innerHeight only in strict mode, but neither Microsoft nor Dottoro says anything about this at their respective innerWidth/innerHeight pages.

When this post was first written the W3C wanted to store the viewport dimensions in document.documentElement.clientWidth/document.documentElement.clientHeight, and

doc_width = document.documentElement.clientWidth;
doc_height = document.documentElement.clientHeight;

does get the viewport width and height with all but one of the OS X GUI browsers on my computer when in strict mode - the exception is Opera 7.50, for which these statements get the page dimensions. (The statements return undefined with IE 5.2.3, but again, IE 5.x doesn't have a strict mode.)

Just looking at these expressions, what would you expect them to give you? Only self.innerWidth/self.innerHeight make sense as a means to obtain the viewport dimensions and they are accordingly supported by pretty much everyone these days. Intuitively both document.body.clientWidth/document.body.clientHeight and document.documentElement.clientWidth/document.documentElement.clientHeight should get the page dimensions, and sometimes they do but other times they don't. And what's with the client business? Why not just width and height? This isn't part of a Microsoft conspiracy to confuse us, is it?

Anyway, before moving on I should note that we discussed a very similar block of viewport-measuring code in the Viewport dimensions section of Blog Entry #240; this code was authored by Quirksmode and was deployed in the lightbox image viewer spotlighted by HTML Goodies' "How To Use the JavaScript Lightbox Image Viewer" tutorial. Quirksmode's code controls access to its innerWidth/innerHeight assignments via a self.innerHeight condition, which unlike the ns6up flag lets in Netscape 4.x, but nobody should be using Netscape 4.x anymore; it controls access to its document.documentElement assignments via a document.documentElement && document.documentElement.clientHeight condition, which converts to true for IE 6+ users in strict mode and to false for other IE users. Altan's use of a more general document.compatMode && document.compatMode != "BackCompat" condition for flagging users in strict mode allows him to functionize it so that he can call on it later.

The doc_width width is used to calculate a set of xp left offsets and the doc_height height is used to calculate a set of yp top offsets as in the original script. Before the snowflakes are repositioned, however, doc_width/doc_height are in the snowIE_NS6( ) function reset per the snowdistance variable; we'll check over these resets, and then roll out a demo, in our next episode.

Comments: Post a Comment

<< Home

Powered by Blogger

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