reptile7's JavaScript blog
Monday, February 13, 2012
 
Lightbox VI
Blog Entry #241

We continue today our discussion of the lightbox image viewer's lightbox.js script and its various functions.

Centering the loading.gif image

After calling the getPageScroll( ) and getPageSize( ) functions, the showLightbox( ) function has at its disposal the following information:
arrayPageScroll[1]: the number of pixels that the lightbox.html page has been scrolled vertically;
arrayPageSize[0]: the width of the lightbox.html page;
arrayPageSize[1]: the height of the lightbox.html page;
arrayPageSize[2]: the width of the viewport; and
arrayPageSize[3]: the height of the viewport.

The showLightbox( ) function approximately centers the loading.gif image - more precisely, its loadingImage img placeholder - in the viewport via the conditional below. (Recall that the loadingImage img was earlier given an absolute positioning by the initLightbox( ) function; we now give it suitable top and left values to finish the job.)

if (objLoadingImage) {
    objLoadingImage.style.top = (arrayPageScroll[1] + ((arrayPageSize[3] - 35 - objLoadingImage.height) / 2) + "px");
    objLoadingImage.style.left = (((arrayPageSize[0] - 20 - objLoadingImage.width) / 2) + "px");
    objLoadingImage.style.display = "block"; }


Execution of the if block statements is conditioned on the availability of the loading.gif image; if the image is AWOL for whatever reason, then the preceding

var objLoadingImage = document.getElementById("loadingImage");

operation (cf. the // prep objects group of statements at the beginning of the showLightbox( ) function, which we covered in Blog Entry #239) returns null, which converts to false in a logical context.

The loadingImage img's normal-flow location (if we were to subtract its position/top/left settings) is in the page's upper-left-hand corner. To vertically center the loadingImage img in the viewport, we'll need to
(a) lower the img such that its top edge coincides with the top edge of the viewport, and then
(b) further lower the img by half the available vertical space in the viewport.
arrayPageScroll[1], which effectively determines the y-axis coordinate of the top edge of the viewport vis-à-vis the top of the page, takes care of part (a).
((arrayPageSize[3] - objLoadingImage.height) / 2)
is what we want for part (b); the extraneous - 35 subtraction operation is taken from the code that vertically centers the lightbox div in the viewport: it should be removed as it causes the loading.gif image to be 18px higher than it should be.

The original getPageScroll( ) function does not take horizontal scrolling into account, and the above objLoadingImage.style.left assignment for horizontally centering the loadingImage img correspondingly does not shift the img rightward such that its left edge coincides with the left edge of the viewport, although we could do that if we wanted to, more specifically:
(1) We could determine the number of pixels that the lightbox.html page has been scrolled horizontally via a getPageScroll( )
xScroll = document.body.scrollLeft; statement,
(2) return arrayPageScroll[0] = xScroll; to the showLightbox( ) function, and
(3) put an arrayPageScroll[0] + addition operation in the objLoadingImage.style.left assignment.

Note that the objLoadingImage.style.left assignment is based on the arrayPageSize[0] page width:
((arrayPageSize[0] - objLoadingImage.width) / 2)
shifts the loadingImage img rightward by half the available horizontal space on the page whether or not the page has been scrolled horizontally (the - 20 subtraction operation is again irrelevant and should be thrown out), which
(a) is OK if the page width is less than or equal to the viewport width (admittedly the case most of the time)
(b) but places the img to the right of where it should be if the page width is greater than the viewport width (in exceptional cases, the img could be out of sight by virtue of being shifted beyond the right edge of the viewport).
To always horizontally center the img in the viewport,

arrayPageScroll[0] + ((arrayPageSize[2] - objLoadingImage.width) / 2) + "px";

is the full expression that we want.

Lastly, the objLoadingImage.style.display = "block"; statement is unnecessary because the loadingImage img wasn't zeroed out in the first place.

Overlay rendering

After the if (objLoadingImage) { ... } block, the showLightbox( ) function sets the height of the overlay div to the height of the lightbox.html page:

objOverlay.style.height = arrayPageSize[1] + "px";

The overlay div's width was earlier set to 100% by the initLightbox( ) function. As explained in the Viewer layout, part 1 section of Blog Entry #237, this width does not cover the beyond-the-viewport part of the page for documents whose widths exceed the viewport width; this problem can now be solved by setting objOverlay.style.width to arrayPageSize[0] + "px";.

Subsequently, the overlay div (which was zeroed out in the first place) and its descendants - the loadingImage img and the objLoadingImageLink anchor element that bridges the overlay div and the loadingImage img - are rendered on the page:

objOverlay.style.display = "block";

The lightbox overlay comprises a horizontally and vertically tiled overlay.png image and is created by the lightbox.css style sheet's
#overlay { background-image: url(overlay.png); } rule.

The url(overlay.png) rule is followed by a distinctly strange block of CSS:

* html #overlay {
    background-color: #333;
    back\ground-color: transparent;
    background-image: url(blank.gif);
    filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="overlay.png", sizingMethod="scale"); }


* html #overlay - at least its * html part - is a nonsensical selector. * is the universal selector, which matches any single element in the document tree. * html therefore selects for any element with an html element descendant, which cannot occur in a valid HTML document because the html element is the root of the document. The * html #overlay declarations are accordingly ignored by Camino, Chrome, Firefox, Netscape 6/7/9, Opera, and Safari on my computer.

The * html #overlay rule set is meant for Internet Explorer, however - it is necessary due to IE's unorthodox support of .png transparency, Lokesh informs us on his "Lightbox JS" page - and I find that IE 5.x does apply the set's first three rules to the overlay div. Here's a rundown of what happens:

(1) The first declaration gives the overlay div a background-color, which is something we should be doing for the other browsers as well: the W3C recommends, When setting a background image, authors should also specify a background color that will be used when the image is unavailable. #333 is a shade of black that approximates the color of the overlay.png image, but there's no reason you couldn't use some other color, of course.

(2) The background-color: #333; declaration is then overwritten by a back\ground-color: transparent; declaration; the backslash shouldn't be there although the browser ignores it. A background-color value of transparent means that the div gets the body element's 'default' background color, typically (but not necessarily) white.

(3) The background-image: url(blank.gif); declaration effectively 'clears' the earlier
#overlay { background-image: url(overlay.png); } rule.
(The lightbox/ package doesn't contain a blank.gif image, and you won't find a blank.gif image at http://lokeshdhakar.com/projects/lightbox/blank.gif.)

As for the filter declaration, I can tell you some things about it:

• The declaration employs the AlphaImageLoader filter, a feature of [Microsoft's] Visual Filters and Transitions [technology], which is deprecated as of Windows Internet Explorer 9.

• The AlphaImageLoader filter fetches the overlay.png image via its src="overlay.png" attribute and then deploys the image between the overlay div's (transparent) background and content (the loading.gif image).

• The overlay.png image is stretched to fill the overlay div via the AlphaImageLoader filter's sizingMethod="scale" attribute.

What I can't tell you is how the declaration works in practice. Microsoft's filter property page states that filter effects are not available on the Macintosh platform and its filter code examples don't work with any of the browsers on my computer. Sorry, Windows users, but you're on your own on this one.

An IE PNG transparency Google search leads to pages saying that
(1) the IE-.png-transparency problem
(a) is relevant to .png images with transparent regions (check out this image demo) and
(b) was sorted out in IE 7 and that
(2) the AlphaImageLoader stuff is meant for IE 5.5/6.
overlay.png is definitely not what you would call a transparent image and thus you wouldn't think that this filtering business would be necessary - indeed, IE 5.x renders overlay.png no differently than do the more modern browsers on my computer - but even if overlay.png were to morph into an opaque color block with some browsers, would that be such a bad thing? Who cares about looking at the underlying page when the lightbox image is on display?

Anyway, let's move on. The lightbox.css style sheet applies a single
#overlay img { border: none; } rule
to the loadingImage img. In days of yore, Netscape rendered a 2px solid #00e border for images, which never bothered me but evidently bugged some people; Firefox has dropped the practice although Camino has held onto it. IE, Chrome/Safari, and Opera do not render borders for images (the CSS border-style property has an initial value of none, after all) and consequently the rule has no effect with these browsers.

Accessing the lightbox images

The showLightbox( ) function's remaining tasks are conditioned on the availability of the lightbox image; these tasks constitute the body of an anonymous function that is triggered when the lightbox image is (pre-)loaded into RAM:

imgPreload = new Image( );
imgPreload.onload = function( ) {
    /* Load the lightbox image into the lightboxImage img, center the lightbox div in the viewport, etc. */ }
imgPreload.src = objLink.href;


This same tactic was used by the initLightbox( ) function to conditionally add the loading.gif and close.gif images to the lightbox. Image preloading should actually be done in the top-level part of a script and not on the fly in response to an event (even a window load event, as was the case in the initLightbox( ) function). Moreover, we can more straightforwardly check the lightbox image availability via the complete property of the Image object, which returns true if the web browser has completed its attempt to load an image and false if it hasn't. Here is one way to deal with these issues:

(1) In the lightbox.html document, give each lightbox link an id, e.g., id="link1", id="snake1", whatever.

(2) Use these ids to index an associative array that is set up to preload the lightbox images.

var docLinks = document.getElementsByTagName("a");
var lightboxImages = new Object( );
for (i = 0; i < docLinks.length; i++) {
    if (docLinks[i].className == "lightboxLink") {
        lightboxImages[docLinks[i].id] = new Image( );
        lightboxImages[docLinks[i].id].src = docLinks[i].href; } }


The preceding script can be run as top-level code if it is placed/referenced after the lightbox links.

(3) The lightbox image availability can now be tested in the showLightbox( ) function via:

if (lightboxImages[objLink.id].complete) { /* Remaining showLightbox( ) tasks */ }

This approach works with either a thumbnail image interface or a text link interface on the lightbox.html page; regarding the former, it works whether the thumbnail images and the lightbox images are the same or different.

A classical JavaScript property that dates to JavaScript 1.1, complete has long had cross-browser support; it's not in the HTML DOM but the W3C has brought it into HTML5.

We'll go through the rest of the showLightbox( ) function in the following entry.

reptile7

Comments: Post a Comment

<< Home

Powered by Blogger

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