reptile7's JavaScript blog
Friday, December 07, 2012
 
December, With or Without Snow
Blog Entry #272

We will get back to our discussion of Altan d.o.o.'s updated falling snow script in just a moment, but first I have a little clarification to make...

Strict mode and IE 5 for Mac

In the Viewport dimensions, take 2 section of the previous post, I claimed, IE 5 doesn't have a strict mode; however, in The solution section of his "Quirks mode and strict mode" tutorial, Quirksmode states that this is only true for IE 5 for Windows and not true for IE 5 for Mac. After running some tests with IE 5.1.6 in the SheepShaver environment, I can now confirm that IE 5 for Mac does in fact have a strict mode that is triggered by placing a document type declaration at the top of a document, its undefined return for document.compatMode notwithstanding; specifically, this strict mode
(a) ignores a CSS length lacking a unit identifier,
(b) refuses to apply a CSS width to a non-replaced inline element (e.g., a span element), and
(c) causes the font size of table cell text to be inherited from the body element and not be based on the browser's default font size.
Regardless of rendering mode, IE 5 for Mac gives an img element an inline rendering and does not support the CSS :hover dynamic pseudo-class.

IE 5 for Mac goes through the ie4up gate (it returns [object Collection] for document.all) but, regardless of rendering mode, returns false for the document.compatMode && document.compatMode != "BackCompat" condition in the iecompattest( ) function and therefore uses document.body.clientWidth and document.body.clientHeight to get the viewport dimensions.

Accessing the snowflake image

In the updated script a snowsrc variable holds the URL of the snowflake image.

// Configure below to change URL path to the snow image
var snowsrc = "snow.gif";


Before the snowflakes are created, snowsrc is reset by a strange line of code:

snowsrc = (snowsrc.indexOf("dynamicdrive.com") != -1) ? "snow.gif" : snowsrc;

Let's assume that you have no connection with the Dynamic Drive code library hosting the script, and that you download the snow.gif image from Dynamic Drive and then upload it to your own www.mysite.com server: in this case the snowsrc.indexOf("dynamicdrive.com") != -1 condition returns false and snowsrc (snow.gif) is assigned to itself - pointless, but no harm, no foul. Alternatively, suppose that you initialize snowsrc to http://www.dynamicdrive.com/dynamicindex3/snow.gif with the intention of calling on the Dynamic Drive-hosted /dynamicindex3/snow.gif image when the time comes to create the snowflakes: in this case the condition returns true and a snow.gif URL for an image you don't have on hand is assigned to snowsrc. Oops.

Download the image, use your local copy for the script, throw out the snowsrc reset.

windowheight vs. pageheight

With the doc_width and doc_height viewport measurements in hand, the yp, sty, xp, am, dx, and stx arrays are initialized exactly as in the original script. Subsequently for browsers that pass through either the ie4up or ns6up gate

if (ie4up || ns6up) { ... }

(1) the snowflakes are written to the page and initially positioned in the page's upper-left-hand corner (see the Snowflake creation section of Blog Entry #269 for the details), and
(2) the snowIE_NS6( ) function is called.

The snowIE_NS6( ) function body begins by resetting doc_width and doc_height, the latter per the value of the snowdistance configuration variable.

function snowIE_NS6( ) {
    doc_width = ns6up ? window.innerWidth - 10 : iecompattest( ).clientWidth - 10;
    doc_height = (window.innerHeight && snowdistance == "windowheight") ? window.innerHeight :
        (ie4up && snowdistance == "windowheight") ? iecompattest( ).clientHeight :
        (ie4up && !window.opera && snowdistance == "pageheight") ? iecompattest( ).scrollHeight : iecompattest( ).offsetHeight; ... }


The doc_width reset is the same as the doc_width determination in the top-level part of the script except that 10 is subtracted from window.innerWidth, document.body.clientWidth, or document.documentElement.clientWidth; without the subtractions you may on occasion - depending on the browser, depending on the presence of a vertical scrollbar - get a snowflake whose xp[i]/am[i] values cause it to bump into the right edge of the viewport and thereby generate a horizontal scrollbar. If desired, the -10 operation(s) can be moved to the xp reset* in the snowIE_NS6( ) snowflake renewal code, i.e., change xp[i] = Math.random( ) * (doc_width - am[i] - 30); to xp[i] = Math.random( ) * (doc_width - am[i] - 40);.

(*I was at first confused by the xp reset but I now see what Altan is doing here, namely, he's using the size of the am[i] neighborhood to control how close the xp[i] left offset can get to the right edge of the viewport. The snow.gif image is 24px by 24px and the thickness of browser scrollbars on my computer is 15px. If the browser gets the viewport width from window.innerWidth and if a vertical scrollbar is present and if the am[i] neighborhood is relatively large, then simply subtracting 50 from doc_width will not be sufficient to keep the snowflakes completely within the viewport.)

As for the doc_height reset, what a statement, huh? I can't recall ever seeing ?: operations daisy-chained in this way. The right-hand part of the assignment is read left to right by the JavaScript engine: let's take it one condition at a time.

(window.innerHeight && snowdistance == "windowheight") ? window.innerHeight :

If the browser supports window.innerHeight AND if the snowdistance variable was set to the string windowheight, meaning that we want the snowflakes to fall as far as the bottom of the viewport (even if the viewport height is less than the page height), then window.innerHeight is assigned to doc_height; if not, we move on to...

(ie4up && snowdistance == "windowheight") ? iecompattest( ).clientHeight :

If the browser's document.all return can be converted to true AND if snowdistance was set to windowheight, then depending on the browser's rendering mode either document.documentElement.clientHeight or document.body.clientHeight is assigned to doc_height; if not, we move on to...

(ie4up && !window.opera && snowdistance == "pageheight") ? iecompattest( ).scrollHeight : iecompattest( ).offsetHeight;

If the browser passes the ie4up test AND if its window.opera return can be converted to false - i.e., if the browser is IE 4+ - AND if snowdistance was set to the string pageheight, meaning that we want the snowflakes to fall to the bottom of the page, then either document.documentElement.scrollHeight or document.body.scrollHeight is assigned to doc_height, and if not, then either document.documentElement.offsetHeight or document.body.offsetHeight is assigned to doc_height, depending on the browser's rendering mode. (Non-IE browsers do make use of the iecompattest( ) function, its name notwithstanding.)

The windowheight conditionals are straightforward so we'll concentrate on the pageheight conditional. We previously wrestled with the scrollHeight and offsetHeight properties in the Page dimensions section of Blog Entry #240: I can tell you what they return on my iMac with precision; as for the Windows side, I can only tell you what they are supposed to return.

The scrollHeight/offsetHeight bifurcation is for the benefit of Netscape 6, which supports offsetHeight but not scrollHeight. Netscape's support for scrollHeight and also for compatMode began with Netscape 7.

According to the Remarks section of Microsoft's "scrollHeight property" page, scrollHeight measures the distance between the top and bottom edges of the object's content; document.body.scrollHeight should therefore give us the height of the body element's content box (sans padding, border, and margin), and that's going to be a good enough pageheight for IE 4+ (or any other browser) as long as the page height is larger than the viewport height.

I was unaware that the body/documentElement dichotomy applies to scrollHeight and offsetHeight - Quirksmode didn't say anything about this on his "Viewport properties" page. However, in the Remarks section of its "html element | html object" page Microsoft states:
With Microsoft Internet Explorer 6 and later, when you use the !DOCTYPE declaration to specify standards-compliant mode, [the html] element represents the canvas - the entire surface onto which a document's contents can be rendered. When you switch on standards-compliant mode, this element also becomes the positioning container for positioned elements that don't have a positioned parent. When the !DOCTYPE declaration does not specify standards-compliant mode, and with earlier versions of Windows Internet Explorer, the body object represents the entire surface onto which a document's contents can be rendered.
The W3C wants scrollHeight to generally return an element's content+padding height; the W3C also specifies:
2. If the element is the root element and the Document is not in quirks mode return max(document content height, value of innerHeight).
3. If the element is the HTML body element and the Document is in quirks mode return max(document content height, value of innerHeight).
So if the page height is smaller than the viewport height, then the document.documentElement.scrollHeight return in strict mode or the document.body.scrollHeight return in quirks mode should be the same as the window.innerHeight return, which is just what we want for the pageheight snowdistance. The "3." list item is consistent with what I observed in Blog Entry #240, with one minor exception: with the browsers on my computer the scrollHeight return for short pages does not include the height of a horizontal scrollbar if present, as the innerHeight return does.

Microsoft's "offsetHeight property" page is somewhat confusing, so let me direct you to W3Schools' offsetHeight definition, which cuts to the chase:
Returns the height of an element, including borders and padding if any, but not margins
The above definition jibes with the W3C's proposed definition of offsetHeight and is consistent with what I observed in almost all cases in Blog Entry #240. Once again, the offsetHeight return will be an acceptable pageheight for non-IE browsers as long as the page height is larger than the viewport height (and it should be OK for IE as well - check out the "offsetHeight property" page's first Example, which outputs a clock display based on a document.body.offsetHeight measurement).

Practical notes, and a demo

There's no real reason for both document.documentElement and document.body assignments to be in the script. A document without a document type declaration can still be valid (in every other sense): you can validate the document with a declaration in place and then subtract the declaration before putting the document on the Web. If you forgo the declaration (the Dynamic Drive "Snow Effect" document doesn't have one), then you can throw out the iecompattest( ) function and its document.documentElement assignments and simplify the code accordingly, e.g.:

var doc_width = ns6up ? window.innerWidth : document.body.clientWidth;
var doc_height = ns6up ? window.innerHeight : document.body.clientHeight;


It is you, the Webmaster, who are setting the snowdistance value, and before deploying the falling snow script you should have a pretty firm idea as to whether the height of the page displaying the snow is smaller or larger than the viewport height: if it's smaller, then you should set snowdistance to windowheight and not pageheight. That said, if you want the snowflakes to fall to the bottom of the viewport for short pages (pages whose heights are smaller than the viewport height) and to the bottom of the page for long pages (pages whose heights are larger than the viewport height), then you really don't need the snowdistance variable. We're already measuring the viewport dimensions in the top-level part of the script; we can also measure the page height in the top-level part of the script and assign it to doc_height in the snowIE_NS6( ) function if it turns out to be larger than the viewport height.

Altan's page height-measuring code is adequate most of the time. However, I know of at least two, admittedly old browsers that are not well-served by this code:
(1) IE 5 for Mac requires offsetHeight and
(2) Opera 7.50 requires scrollHeight
to measure the height of the page for long pages. Alternatively, these browsers - and all of the other relevant browsers on my computer - are nicely accommodated by the page height-measuring code at Quirksmode's "Viewport properties" page, which compares the document.body.scrollHeight and document.body.offsetHeight returns and uses whichever value is larger.

var test1 = document.body.scrollHeight;
var test2 = document.body.offsetHeight;
var page_height = (test1 > test2) ? test1 : test2;
function snowIE_NS6( ) { ...
    if (page_height > doc_height) doc_height = page_height; ... }


This approach can also be applied to pages whose widths exceed the viewport width.

Time for a demo. The Dynamic Drive "Snow Effect" page illustrates the snowdistance=pageheight effect (assuming that the viewport height is less than the page's 1447 document.body.scrollHeight, which should be the case for most if not all users) but it doesn't illustrate the hidesnowtime effect, so I'll do that. In the div below, click the button to see some falling snow that will disappear in seven seconds.




My demo code dispenses with the ie4up and ns6up flags: it will admittedly throw errors with seriously old browsers that no one should be using anymore. (Back in the day Altan's code would have thrown an error with IE 4.x, which is green-lighted by the ie4up flag but does not support the document.getElementById( ) method.)

We're not quite done with Altan and his snow scripts. Lissa has adapted Altan's original code for a rising bubbles display - we'll look at that adaptation in the following entry.

Comments: Post a Comment

<< Home

Powered by Blogger

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