reptile7's JavaScript blog
Wednesday, December 26, 2012
 
Not on the Menu
Blog Entry #274

We return now to our discussion of Lissa Explains It All's up.html demo page. In the demo source the marquee code is followed by a center element bearing a request for the demo's visitors:

<center>Do not remove the graphics from this page please, I made them for this page only =)</center>

In the hope of preventing the user from downloading the mfish1.gif/mfish2.gif images by right-clicking them, popping up a context menu, and saving them (presumably this also applies to the bubble images, but you have to be pretty fast to right-click those guys)
A context menu with a highlighted 'Save Image As...' command
Lissa places the script below in the document head:

/* No right-click script v.2.5; © 1998 barts1000; barts1000@aol.com
Provided free at http://www.lissaexplains.com - don't delete this header! */

// Message for the alert( ) box
var message = "Sorry, that function is disabled.\n\nContents & Graphics Copyright ©Lissa\nMy work is not Public Domain, and should NOT be taken from this site.";


// Don't edit below!
function click(e) {
    if (document.all) {
        if (event.button == 2) {
            window.alert(message);
            return false; } }
    if (document.layers) {
        if (e.which == 3) {
            window.alert(message);
            return false; } } }


if (document.layers) document.captureEvents(Event.MOUSEDOWN);
document.onmousedown = click;


The preceding script was authored by "barts1000" and at a time (1998) when the current versions of Internet Explorer and Netscape were IE 4.x and Netscape 4.x, respectively. A document.onmousedown = click; statement causes the click( ) function to listen for mousedown events that occur anywhere in the document content area. Right-clicking a demo image calls the click( ) function, which displays a "don't take my stuff" message on an alert( ) box and returns false so as to end the event handling for the triggering mousedown event and thereby prevent a context menu from popping up. With most browsers, context menus are generated by mousedown events and not by click events and would therefore not be suppressed by a document.onclick = click; click( ) trigger.

Browser differences:
• IE 4.x goes through the document.all gate and then checks the value of the then-proprietary, now-standard event.button property, which returns 2 for a right-click.
• Netscape 4.x goes through the document.layers gate and then checks the value of the now cross-browser but still not standard e.which property, which returns 3 for a right-click.
• For both IE 4.x and Netscape 4.x, mousedowning on an image dispatches the mousedown event to the underlying img element. Netscape 4.x does not support the onmousedown event handler for the client-side Image object and does not support event bubbling, so it must capture mousedown events at the level of the window or document object in order to bind an image mousedown to the click( ) function.

FYI: If you're going to put a non-ASCII character - in this case a copyright symbol - in a string literal, then the safe and reliable way to do so is to encode the character as a Unicode escape sequence, i.e., ©Lissa should be formulated as \u00A9Lissa.

Although not labeled "Windows only", the barts1000 script as originally written does not work on the Mac platform. Back in the day Apple's mice had only one button, and the Classic Mac browsers on my iMac accordingly do not recognize right-button mouse events qua right-button mouse events, e.g., a right-click is interpreted as an ordinary (left-)click. However, control-clicking a rendered element does display a context menu with Classic Mac browsers. I find that IE 4.5 and Netscape 4.61 do not interpret control-clicks as right-clicks but Netscape 6.2.3 and Netscape 7.02 do: with the latter two browsers the barts1000 script works as advertised upon changing the document.layers gate to a document.getElementById && !document.all gate, i.e., the alert( ) message pops up and a context menu doesn't.

As for the OS X GUI browsers on my computer, they all recognize right-button mouse events and they all display a context menu upon control-clicking a rendered element. The two oldest of these browsers, IE 5.2.3 and Opera 7.50*, disable JavaScript for control-click and right-click events - this is not so odd given that they predate the Mighty Mouse**, the first Apple mouse with a right-button capability - they won't even call the click( ) function, let alone go through the document.all gate, with a control-click or right-click. The rest of the OS X gang go through a document.getElementById && !document.all gate and generally display the alert( ) message but the return false; statement does not prevent a context menu from popping up (with one exception - the script does suppress right-click-generated context menus when using Camino - but in all other cases I get a context menu).

*Turning on Opera 7.50's
Allow script to receive right clicks
Preferences option has no effect on my computer.

**I myself use a Mighty Mouse; its 'right button' is enabled via the Keyboard & Mouse pane of the System Preferences application:
Enabling the Mighty Mouse's 'right button'
A newer script

The barts1000 script is offered as a standalone unit of code by the How do I disable the right click? subsection of Section 6 of the JavaScript subsector of Lissa Explains It All; the subsection also provides a second, slightly newer 'no right-click' script that takes us in the direction we want to go:

if (window.Event) document.captureEvents(Event.MOUSEUP);

function nocontextmenu( ) {
    event.cancelBubble = true;
    event.returnValue = false;
    return false; }


function norightclick(e) {
    if (window.Event) {
        if (e.which == 2 || e.which == 3) return false; }
    else if (event.button == 2 || event.button == 3) {
        event.cancelBubble = true;
        event.returnValue = false;
        return false; } }


if (document.layers) document.captureEvents(Event.MOUSEDOWN);
document.oncontextmenu = nocontextmenu;
document.onmousedown = norightclick;
document.onmouseup = norightclick;


Lissa does not specify the second script's author; admittedly, tracking down script authors can often be difficult. She also doesn't give a date for the script, although the script's combination of features indicates that it was written in either 1999 or 2000.

The second script's all-important feature is the oncontextmenu event handler, which was first implemented by Microsoft in IE 5 for Windows (IE 5 for Mac doesn't support it), was subsequently picked up by Netscape for Netscape 6, and now has cross-browser support, and is being brought into HTML5 (nope, it didn't make the cut - the WHATWG guys are currently working on it). The display of a context menu constitutes a contextmenu event, which can be tied to the execution of JavaScript code by the oncontextmenu event handler. Like most mouse-related events, the contextmenu event can be canceled, and the script's nocontextmenu( ) function aims to do just that.

The nocontextmenu( ) function is meant for IE 5+ for Windows - note its use of the event keyword for referencing the event object. I can't tell you if the cancelBubble and returnValue assignments*** are necessary with IE, but what I can tell you is that the return false; statement by itself successfully suppresses the display of context menus with Firefox, Google Chrome, Safari, Opera 12, Camino, and Netscape 6/7/9 on my computer, as would be expected from the first Example on Mozilla's "GlobalEventHandlers.oncontextmenu" page.

***Microsoft's own oncontextmenu examples don't use this code and I am thus not inclined to discuss it, but let me make two quick comments thereon:
(1) If we're going to register the nocontextmenu( ) listener on the document object, then we shouldn't be setting event.cancelBubble to true, i.e., we will want the contextmenu event to bubble up from the img target to the document object.
(2) Setting event.returnValue to false can be done in place of the return false; operation (there's no need for both statements to be there) for non-Mozilla browsers. Leaving Firefox/Camino users out in the cold would not be cool, so we should keep the return false; statement and lose the returnValue assignment.

The accompanying norightclick( ) function was written for Netscape 4.x and IE 4.x; interestingly, it targets both the middle button and the right button for a three-button mouse. After going through a window.Event gate, Netscape 4.x checks the e.which value and then returns false if the value is either 2 or 3. For its part, IE 4.x tests if event.button is 2 or 3 - these values should be 1 or 2, respectively - and then runs through the same set of statements that makes up the nocontextmenu( ) function body.

Leaving aside the issue of what a middle-click might do on your computer (on my computer, a middle-click launches the Dashboard application by default), there is no point in holding onto the norightclick( ) function as no one should be using those browsers without oncontextmenu support. Indeed, we should be able to reduce the second script to a single line of code:

document.oncontextmenu = function ( ) { return false; }

Mozilla notes that the Events DOM's Event interface has a preventDefault( ) method that can also be pressed into service in this regard:

document.oncontextmenu = function (e) { e.preventDefault( ); }

Alternatively, we can equip each up.html img element with an oncontextmenu="return false;" attribute - see the first Example on the aforelinked Microsoft oncontextmenu page.

<img id="fish0" width="75" height="50" src="mfish1.gif" alt="fish" oncontextmenu="return false;">

Try it out on the image below - nothing should happen when you right-click it.

fish

Relatedly, giving the body element an onmousedown="return false;" attribute will (for newer browsers, at least) throw up a roadblock preventing the user from mousically selecting text on a Web page. Try to select the text in the sentence below:

The quick brown fox jumps over the lazy dog.

Regarding the right-click-disabling scripts, Lissa concedes:
[These scripts are] not foolproof. If someone really wants something from your page they can find ways around it, but at least it's a warning to people who want to take your graphics.
All a user has to do is turn off the browser's JavaScript engine via the Preferences pane and - *poof* - there goes the script. (In my case, I didn't even do that - I just went to the up.html/image URL locations and then downloaded the files via the File menu's Save command.)

We have one last falling snow script to cover: the How do I make snow fall on my Web site? subsection of Section 1 of the JavaScript subsector offers a "cross-browser snowflake script" that we'll take on in the next post.

Sunday, December 16, 2012
 
Voyage to the Bottom of the Viewport
Blog Entry #273

In response to reader requests, Lissa modified Altan d.o.o.'s original falling snow script so that its images "fall" from the bottom to the top of the viewport instead of from top to bottom: the resulting code is the third script offered by Section 1 of the JavaScript subsector of Lissa Explains It All. A .zipped bubbleinstructions.txt file containing the script plus some commentary can be downloaded here.

Lissa provides an up.html demo page that illustrates the script with a set of bubble images. Interestingly, Lissa adds to her demo two fish marquees that 'swim' across the viewport as the bubbles rise; the marquee code does not appear in the bubbleinstructions.txt file. The marquee part of the demo was IE only at the time the script was written - Netscape's support for the marquee element only goes back as far as Netscape 7 - although the bubbles part could be run by both IE and Netscape.

In this post we'll detail the differences between Altan's original script and Lissa's modified script and also examine the added marquee code, with a demo to follow.

The differences

Not so important

The modified script replaces the original script's var snowflake = "snow.gif"; image with a snow array of three images.

var snow = new Array( );
snow[0] = "yourfilename1.gif";
snow[1] = "yourfilename2.gif";
snow[2] = "yourfilename3.gif";


For her demo, Lissa fills the array with bubble1.gif, bubble2.gif, and bubble3.gif images.

Lissa ups the no "snow number" to 30

var no = 30; // Snow number - no is 15 in the original script.

and uses a 30-iteration for loop to write 10 sets of the snow images to the page. (It's not necessary for no to be an even multiple of snow.length, although I myself would have done it that way.)

var i, j = 0;
for (i = 0; i < no; ++i) { ...
    document.write("<div id='dot" + i + "' style='position:absolute;z-index:" + i + "visibility:visible;top:15px;left:15px;width:1;'><img src='" + snow[j] + "' border='0'></div>");
    if (j == (snow.length - 1)) { j = 0; } else { j += 1; } }


The snow images are written in a snow[0]-snow[1]-snow[2] cycle per the value of a j index, which is initialized to 0 and is incremented or reset by the if...else statement at the end of the loop.

The width:1; div style is new, but I have no idea what it's doing there. This declaration is ignored in strict mode because the width value doesn't have a unit identifier; it causes the divs to collapse to 1px-thick vertical lines in quirks mode, but this isn't a problem because the snow images horizontally overflow the divs and are rendered normally. Throw it out.

Important

In the modified script's snowIE( )/snowNS( ) functions, the sty[i] vertical step is iteratively subtracted from the yp[i] top offset.

for (i = 0; i < no; ++i) { yp[i] -= sty[i]; ... } // sty[i] is added to yp[i] in the original script.

For the functions' image renewal code, the modified script lets each image rise to a top value of -50px before renewing the image and then drops the image to a top value of doc_heightpx for its next rise.

for (i = 0; i < no; ++i) { ...
    if (yp[i] < -50) {
        yp[i] = doc_height; ... } }
/* yp[i] maxes out at doc_height-50 and is reset to 0 in the original script. */


(These renewal boundaries are a bit overkill IMO - for the 24px-by-24px bubble images of Lissa's demo, I would trigger renewal at yp[i] < 0 and restart the images at yp[i] = doc_height - 24 - but to each her own.)

Marquee madness

In the up.html document body the rising bubbles script is followed by the marquee code below:

<marquee behavior="scroll" direction="right" scrollamount="2">
<image src="chimage.php?image=mfish1.gif" width="75" height="50" alt="fish" border="0"></a>
</marquee><br><br><br>
<marquee behavior="scroll" direction="left" scrollamount="2">
<image src="chimage.php?image=mfish2.gif" width="75" height="50" alt="fish" border="0"></a>
</marquee>


(The </a> tags following the image elements do not have corresponding start-tags and should be removed - we shouldn't be putting a link in a marquee anyway.)

The first marquee should show two right-facing fish - a leading large fish and a trailing small fish - slowly swimming rightward across the viewport. About 55px beneath the first marquee the second marquee should show a left-facing large fish slowly swimming leftward across the viewport. Upon testing the up.html page with Opera 7.50, a browser that gets through the var ie4up = (document.all) ? 1 : 0; gate, I find that the rising bubbles action is A-OK but there are empty placeholders where the marquee images should be.

Note that the image elements are named image and not img - Lissa didn't 'write' this code with (shudder) an HTML editor, did she? But curiously enough, every GUI browser on my computer, in both the OS X and SheepShaver environments, accepts image in place of img, so that's not the culprit.

As you might expect, the image src URLs are where the problem lies. These URLs seem to point to a PHP script that presumably loads mfish1.gif and mfish2.gif images into their respective placeholders. However, http://www.lissaexplains.com/chimage.php?image=mfish1.gif,
http://www.lissaexplains.com/chimage.php?image=mfish2.gif, and
http://www.lissaexplains.com/chimage.php all route me to the Lissa Explains It All home page - whatever chimage.php was, it's gone.

As the bubble images are located in the www.lissaexplains.com hostname directory, I decided to see if http://www.lissaexplains.com/mfish1.gif and
http://www.lissaexplains.com/mfish2.gif would respectively lead me to the mfish1.gif and mfish2.gif images. Yes! Success!

But do we want to be using marquees in the first place? Per Blog Entry #163 (see The W3C and the marquee element subsection), HTML5 has obsoleted the marquee element, its widespread support notwithstanding. (Actually, the version of Safari on my computer - Safari 5.0.6 - strangely refuses to run the two marquees simultaneously; unrelatedly, it has also dropped support for the alternate marquee behavior.) Fortunately, a JavaScript equivalent is at hand...

left it

For the next section's demo, I
(a) give the mfish1.gif img element an id="fish0" and the mfish2.gif img element an id="fish1" and
(b) position:absolute; the elements, and then
(c) use the script below to move the images across the viewport.

var fish0 = document.getElementById("fish0");
var fish1 = document.getElementById("fish1");
var leftnumber0, leftnumber1;
fish0.style.left = "-75px";
fish1.style.top = "115px";
fish1.style.left = doc_width + "px";
window.onload = function ( ) { window.setInterval("moveFish( );", 80); }


function moveFish( ) {
    leftnumber0 = parseInt(fish0.style.left, 10);
    leftnumber1 = parseInt(fish1.style.left, 10);
    if (leftnumber0 < doc_width) fish0.style.left = (leftnumber0 + 2) + "px";
    else fish0.style.left = "-75px";
    if (leftnumber1 > -75) fish1.style.left = (leftnumber1 - 2) + "px";
    else fish1.style.left = doc_width + "px"; }


(Both mfish1.gif and mfish2.gif have a width of 75px - that's what the 75 is about.)

The above script is based on the code samples in HTML Goodies' "How to Create a JavaScript Animation" tutorial, which we covered in Blog Entries #209 and #210. The fish0.style.left offset is initialized to -75px and the fish1.style.left offset is initialized to doc_widthpx; a moveFish( ) function increments the former offset so as to shift the fish0 fish rightward and decrements the latter offset so as to shift the fish1 fish leftward. Pretty straightforward.

Demo

February 2017 Update: For the restored demo below, the fish left offsets range from "0px" to doc_width2 - 75 + "px" so as to prevent the generation of a horizontal scrollbar when the fish are at the left/right edges of the div - check the page source for the full coding.

So here's what you should see at the up.html page (some slight variations between the displays notwithstanding):



fish
fish



There's one more up.html code difference that we haven't discussed yet. The up.html document head contains a script whose purpose is to cancel right-click events - we'll go through this script in detail in the following entry.

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.


Powered by Blogger

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