reptile7's JavaScript blog
Wednesday, March 27, 2013
 
Light Up the Sky
Blog Entry #283

In today's post we will take up the mouse fireworks script offered by Section 8 of the JavaScript subsector of Lissa Explains It All. The mouse fireworks script was authored by Matt Gabbert and, on the basis of its browser-screening code, evidently goes back to the 1999-2000 period when the current versions of IE and Netscape were IE 5.x and Netscape 4.x, respectively. In response to a click on the page, the mouse fireworks script projects from the click point a fountain-like burst of text or images - the script's effect more resembles a geyser than an actual fireworks display, IMO.

Contra the How do I make mouse fireworks? text, the script doesn't contain any copyright information. Regarding the mgabbert@usrtoday.com email address that Lissa provides for Matt, the usrtoday.com domain is now usrtoday.org - I have no idea if Matt is still living in Upper Saddle River, New Jersey. Matt used to run a NoLag.com site, which is gone and is not accessible via but can now be viewed at the Internet Archive.

The projectiles and Lissa's demo

Lissa provides for the mouse fireworks script a fireworks.shtml demo that works with IE 4+, Netscape 4.x, and Opera 5-9.2. For her projectiles Lissa uses a set of ten colored copyright symbols that will display as ♥ characters if the Symbol typeface is available.

<div id="sparks">
<div id="sDiv0" style="position:absolute;visibility:hidden;"><font face="symbol,wingdings,arial" color="#ff00ff" size="1">©</font></div>
<div id="sDiv1" style="position:absolute;visibility:hidden;"><font face="symbol,wingdings,arial" color="#9463f7" size="1">©</font></div>
<div id="sDiv2" style="position:absolute;visibility:hidden;"><font face="symbol,wingdings,arial" color="#319cff" size="1">©</font></div>
...
<div id="sDiv8" style="position:absolute;visibility:hidden;"><font face="symbol,wingdings,arial" color="#00ff00" size="2">©</font></div>
<div id="sDiv9" style="position:absolute;visibility:hidden;"><font face="symbol,wingdings,arial" color="#ffc0ff" size="2">©</font></div>
</div>


The ♥ character, termed the "black heart suit" or "valentine" character by the W3C, has long been standard - it was given its own parameter entity definition in HTML 4 (see the next-to-the-last entry in this list) and its hexadecimal Unicode code position is U+2665 - and can be rendered via a &hearts; or &#9829; reference.
FYI: IE 5.x supports both &hearts; and &#9829;; IE 4.x supports the latter but not the former; Netscape 4.x doesn't support either of them.

As for the other font element face values:
(a) In the Wingdings typeface the © character maps onto a three pointed black star, which is not now a Unicode character (U+1F7C2).
(b) If you did want a burst of © characters, then Arial is a perfectly suitable typeface, of course.

You may know that the W3C names the #ff00ff color "fuchsia" and the #00ff00 color "lime"; the other three colors - #9463f7, #319cff, and #ffc0ff - do not have recognized color names (I couldn't find them in Wikipedia's "Lists of colors" entries).

We noted in the previous entry that a size="1" font element attribute is equivalent to a font-size:x-small; style declaration; similarly, a size="2" font element attribute is equivalent to a font-size:small; style declaration. These font-size values are way too small for my taste and I crank them up to 36px and 48px respectively for the demo below.

The © characters are wrapped in div elements so that we can apply CSS to them. The div wrappers are given ordinalized ids: sDiv0, sDiv1, sDiv2, etc. The script makes no use of the outer sparks div, which can be removed if desired. I myself would hold onto the sparks div as the CSS child selector mechanism allows us to use it as a 'class' for applying styles to its child divs:

#sparks > div { position: absolute; visibility: hidden; }
/* If your projectiles are all the same color and size, then you can put that information here too. */


So, for those of you for whom Lissa's demo does not work, click in the div below to see what it looks like.
(As noted above, my ♥s are a lot bigger than Lissa's ♥s; also, I don't care for Lissa's #ffcc00 background color so I've changed it to aliceblue.)


Click any spot in this div.

Browser screening and fireworks monitoring

The mouse fireworks script's JavaScript begins with the following set of variable declarations:

var ver = navigator.appVersion;
var dom = document.getElementById ? 1 : 0;
var ie5 = (ver.indexOf("MSIE 5") > -1 && dom) ? 1 : 0;
var n = (document.layers);
var ie = (document.all);
var sparksAflyin = 0;
var totalSparks = 0;
var sparksOn = 1;


The n variable safeguards some Netscape event model code in the script's initMouseEvents( ) and mouseDown( ) functions and some layer object code in the script's moveTo( ) function. The ie variable safeguards some document.body and document.all code in the script's moveTo( ) function.

Even at the time the script was written, the ver, dom, and ie5 declarations could have been thrown out as no use is made of the ie5 variable. I noted earlier that the script works with IE 4.x. On the IE side the © divs are accessed via document.all.divID expressions; accessing the divs with the getElementById( ) method would have frozen out IE 4.x users.

The sparksAflyin variable is a boolean whose 1 or 0 value respectively signifies that a fireworks burst is or is not in progress, whereas the totalSparks variable is a counter that tracks the end of a fireworks burst; together these variables prevent the user from setting off more than one fireworks burst at the same time - as you can imagine, 'stacking' the fireworks bursts gives a pretty spastic display.

The sparksOn variable is initialized to 1 and remains at 1 as long as the user is at the mouse fireworks page; it can be thrown out as its role in the mouseDown( ) function - safeguarding the mouseDown( ) function body - is unnecessary.

Moving on, the preceding declarations are followed by five functions - in order, initMouseEvents( ), mouseDown( ), moveTo( ), SHOW( ), and HIDE( ) - and then a set of twenty arrays whose values are used to calculate and set offsets for the fireworks projectiles; we'll discuss the arrays when we go through the moveTo( ) function.

Pre-burst

Loading the mouse fireworks page

<body onload="initMouseEvents( );">

calls the initMouseEvents( ) function, which coassociates the mouseDown( ) function, mousedown events, and the document object:

function initMouseEvents( ) {
    document.onmousedown = mouseDown;
    if (n) document.captureEvents(Event.MOUSEDOWN | Event.MOUSEMOVE); }


For Netscape 4.x, which does not support event bubbling, it is necessary to capture mousedown events at the level of the document or window object if you'd like to set off a fireworks burst by clicking any spot on this page, but unless I'm missing something it shouldn't be necessary to likewise capture mousemove events (there are no onmousemove event handlers in the script).

So let's click on the page and get the fireworks action under way, shall we? Our click - more precisely, its mousedown component - calls the mouseDown( ) function, whose first order of business is to get the click's page coordinates (vis-à-vis its viewport coordinates):

function mouseDown(e) {
    if (sparksOn) { /* As noted above, the sparksOn gate is excess baggage. */
        var mousex = (n) ? e.pageX : event.x + document.body.scrollLeft;
        var mousey = (n) ? e.pageY : event.y + document.body.scrollTop; ... } ... }


On the Netscape side we get the click coordinates via the e object's pageX and pageY properties - so far, so normal.

On the IE side we get the click coordinates via the event object's x and y properties, whose returns are respectively adjusted by document.body.scrollLeft and document.body.scrollTop in order to take user scrolling into account. According to Microsoft, the x and y properties play out differently with IE 4.x and IE 5.x, specifically, they should return a click's viewport coordinates with IE 4.x and its page coordinates with IE 5.x (if the click's target element does not have a relatively positioned ancestor). In practice on my iMac, event.x and event.y return a click's viewport coordinates when using IE 4.5 and IE 5.1.6.

The x and y properties are today also supported by Google Chrome, Safari, and Opera, and they also return a click's viewport coordinates with these browsers - Mozilla's browsers currently don't support them - they are not standard but are on track to be standardized. Somewhat ironically, Netscape equipped its first-generation event object with x and y properties that could have been used by the mouseDown( ) function to get a click's page coordinates on the Netscape side way back when; however, those properties are only supported by Netscape 4.x.

You can hold onto the event.x and event.y expressions if you want - if the display area doesn't exceed the viewport, as is the case for Lissa's demo and my demo above, then the viewport coordinates vs. page coordinates thing isn't going to be an issue anyway - although I myself would respectively replace x and y with clientX and clientY, which reliably return a mouse event's viewport coordinates with all of the major browsers. Netscape's clientX/clientY support began with Netscape 6 and its scrollLeft/scrollTop support began with Netscape 7. Meanwhile, IE 9+ supports the Netscape-cum-W3C event model and the pageX/pageY properties but earlier IE versions don't. Putting it all together, we should be able to accommodate pretty much everybody with:

mousex = e ? e.pageX : event.clientX + document.body.scrollLeft;
mousey = e ? e.pageY : event.clientY + document.body.scrollTop;


With the click coordinates in hand, the mouseDown( ) function next checks if a fireworks burst is not in progress

if (!sparksAflyin) { ... }

and if the coast is clear it iteratively calls on the SHOW( ) function to visibilize the © divs:

for (var k = 0; k <= 9; k++)
    eval('SHOW("sDiv' + k + '")');
...
function SHOW(divName) {
    if (document.all) /* Why document.all and not ie? I don't know. */
        eval('document.all.' + divName + '.style.visibility = "visible";');
    else if (document.layers) /* Why document.layers and not n? I don't know. */
        eval('document.layers["' + divName + '"].visibility = "visible";'); }


Are any of the preceding uses of the eval( ) function necessary? Nope:

for (var k = 0; k <= 9; k++) SHOW("sDiv" + k);
...
function SHOW(divName) {
    if (ie) document.all(divName).style.visibility = "visible";
    if (n) document.layers[divName].visibility = "show"; }


As shown above, the layer object's 'true' visibility value is actually show and not visible; in this case the Netscape 4.x JavaScript engine replaces* visible with show, the initial visibility value for a top-level layer.
(*You can confirm this by running a window.alert(document.sDiv0.visibility); command.)

Subsequently the mouseDown( ) function turns on the sparksAflyin boolean and unnecessarily resets the totalSparks counter to 0:

sparksAflyin = 1;
totalSparks = 0; /* This line can be commented out. */


We are now ready to get those fireworks projectiles moving, and we'll go through the details thereof in the following entry.

Comments: Post a Comment

<< Home

Powered by Blogger

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