reptile7's JavaScript blog
Saturday, January 21, 2012
 
Lightbox IV
Blog Entry #239

At this point - when the lightbox.html page has loaded - we have largely coded the lightbox image viewer; we still have to
(a) set the height of the overlay div,
(b) center the loading.gif image and the lightbox div in the viewport,
(c) load an image into the lightboxImage img placeholder,
(d) write a caption (if relevant) to the lightboxCaption div,
(e) set up the press <kbd>x</kbd> to close behavior, and of course
(f) bring the whole shebang to life as it's all currently zeroed out.

We'll get to each of these operations in due course. Meanwhile, the lightbox.html page's rel="lightbox" links - be they thumbnail image links or textual links - are now sitting in front of us, ready for action. When a lightbox link is clicked, the lightbox.js showLightbox( ) function is called and, via the this keyword, passed the link by the following conditional in the lightbox.js initLightbox( ) function:

if (anchor.getAttribute("href") && (anchor.getAttribute("rel") == "lightbox")) {
    anchor.onclick = function ( ) { showLightbox(this); return false; } }


The showLightbox( ) function first gives the passed link object an objLink identifier and then 'gets' six of the lightbox image viewer's elements:

function showLightbox(objLink) {
    var objOverlay = document.getElementById("overlay");
    var objLoadingImage = document.getElementById("loadingImage");
    var objLightbox = document.getElementById("lightbox");
    var objImage = document.getElementById("lightboxImage");
    var objLightboxDetails = document.getElementById("lightboxDetails");
    var objCaption = document.getElementById("lightboxCaption");


In source order, the overlay div, the loadingImage img, the lightbox div, the lightboxImage img, the lightboxDetails div, and the lightboxCaption div are given objOverlay, objLoadingImage, objLightbox, objImage, objLightboxDetails, and objCaption identifiers, respectively. If you've been keeping score at home, you will note that these are the very same variable names that these elements were given when they were created in the initLightbox( ) function; it follows that the above getElementById( ) statements can be thrown out (1) if the obj- variables are declared globally

var loadingImage = "loading.gif";
var closeButton = "close.gif";
var objOverlay, objLoadingImage, objLightbox, objImage, objLightboxDetails, objCaption;
function getPageScroll( ) { ... }


and (2) if we subtract the var keywords that precede the declarations of these variables in the initLightbox( ) function.

function initLightbox( ) { ...
    objOverlay = document.createElement("div"); ...
    objLoadingImage = document.createElement("img"); ...


Next, the showLightbox( ) function places calls to the lightbox.js getPageScroll( ) and getPageSize( ) functions:

var arrayPageScroll = getPageScroll( );
var arrayPageSize = getPageSize( );


The getPageScroll( ) function determines via a three-clause conditional how many pixels the page has been scrolled vertically; its arrayPageScroll return will be used to set top values that vertically center the loading.gif image and the lightbox div in the viewport. Here's the getPageScroll( ) function:

/* Returns array with x,y page scroll values. Core code from - quirksmode.org */
function getPageScroll( ) {
    var yScroll;
    if (self.pageYOffset) {
        yScroll = self.pageYOffset; }
    else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
        yScroll = document.documentElement.scrollTop; }
    else if (document.body) { // all other Explorers
        yScroll = document.body.scrollTop; }
    arrayPageScroll = new Array("", yScroll);
    return arrayPageScroll; }


Contra the /* Returns array with x,y page scroll values. */ comment, the getPageScroll( ) function does not address horizontal (x-axis) scrolling. The /* Core code from - quirksmode.org */ comment is referring to an http://www.quirksmode.org/viewport/compatibility.html "Viewport properties" page that is no longer hosted by QuirksMode.org but, courtesy of the Internet Archive, can still be seen here (this is the 30 December 2005 version of the page, which was contemporary with the development of the lightbox image viewer in late 2005).

The if (self.pageYOffset) yScroll = self.pageYOffset; clause tests the browser's support for the pageYOffset property of the window object and then assigns the pageYOffset return to a yScroll variable if the page has been scrolled vertically. A classical JavaScript property that dates to JavaScript 1.2, pageYOffset is today supported by the most recent versions of all of the major browsers, including Internet Explorer. The W3C will be adding pageYOffset to an HTML5 Window interface.

There's just one little problem with the if (self.pageYOffset) test: if the user didn't do any vertical scrolling before clicking a lightbox link (as would be the case at the "Lightbox" tutorial's isolated demo page), then self.pageYOffset will return 0, a value that converts to false in a logical context. Fortunately, the else if (document.body) yScroll = document.body.scrollTop; clause will with most browsers deliver a 0 to yScroll in this case, but if for whatever reason you did want to work with the pageYOffset property, then a

if (/\d+/.test(window.pageYOffset)) yScroll = window.pageYOffset;
/* I trust y'all know that the window and self object references are synonymous. */


clause will provide you with the true condition return you are looking for.

The pageYOffset property is not supported by pre-IE 9 versions of Internet Explorer, ergo the two else if clauses. Both of these clauses work with the scrollTop property, which was first implemented by Microsoft in IE 5; they respectively assign document.documentElement.scrollTop and document.body.scrollTop to yScroll for a true condition return.

The documentElement attribute of the Document interface, which goes back to Level 1 of the Core DOM, references the html element of an HTML document. The body attribute of the HTMLDocument interface, which goes back to Level 1 of the HTML DOM, normally (in the absence of frames) references a document's body element. Microsoft's scrollTop page states that scrollTop applies to both the body and html elements. However - and as a Mac user I am not able to confirm this - the QuirksMode "Viewport properties" page reports that when IE 6 is running in "strict mode" (a standards-compliant mode), it doesn't support document.body.scrollTop, notwithstanding that document.body was no less W3C-kosher than document.documentElement was at the time IE 6 was released (2001), whereas for all other IE 5+ versions document.body.scrollTop is OK.

IE 6's strict mode is triggered by placing a valid document type declaration at the top of a document, for example:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">


If the declaration is invalid or if it isn't there in the first place, then IE 6 runs in "quirks [IE 5-compatible] mode". According to Wikipedia's "Quirks mode" entry, IE 6's strict mode can be switched to quirks mode by preceding the declaration with an XML prolog or a comment, thereby providing a mechanism via which we can get rid of the document.documentElement code and route all IE 6 users through the else if (document.body) yScroll = document.body.scrollTop; clause.

(The following paragraph is somewhat outdated but I'm going to leave it intact 'for historical purposes':
Of course, this being the year 2012, you may be wondering why we would be interested in accommodating IE 6 users at all. As it happens, Microsoft itself maintains an "Internet Explorer 6 Countdown" page on which it proclaims that it's time to say goodbye (bold in original) to IE 6 and that friends don't let friends use Internet Explorer 6 (there's even a Death to IE 6 in the page's title bar), so if you want to stiff the 7.7% of the world that is still using IE 6 and chuck that first else if clause, then our friends in Redmond have got your back.)

As noted above, the scrollTop property today has widespread support (its Netscape support goes back to Netscape 7); accordingly, the W3C will be adding it to the Core DOM's Element interface. In both the OS X and SheepShaver environments, the browsers on my computer that support scrollTop do so for the body element and not for the html element; interestingly, I find that these browsers uniformly return 0 (not undefined) for document.documentElement.scrollTop whether or not I've done any vertical scrolling, and thus they would pass a /\d+/.test(document.documentElement.scrollTop) test for IE 6 users* in the absence of the pageYOffset code. I would therefore argue that we should scrap both the pageYOffset code and the document.documentElement code and reduce the y-axis part of the getPageScroll( ) function to:

if (/\d+/.test(document.body.scrollTop)) yScroll = document.body.scrollTop;

*Like the self.pageYOffset condition, the document.documentElement && document.documentElement.scrollTop condition converts to false if the page hasn't been scrolled vertically.

x-axis

The Scrolling offset subsection of the QuirksMode "Viewport properties" page gives commands that can determine how many pixels a page has been scrolled horizontally:

var x;
if (self.pageYOffset) {
    x = self.pageXOffset; }
else if (document.documentElement && document.documentElement.scrollTop) {
    x = document.documentElement.scrollLeft; }
else if (document.body) {
    x = document.body.scrollLeft; }


For the getPageScroll( ) function, the author subtracted the pageXOffset/scrollLeft statements evidently on the sensible assumption that the document width will not exceed the viewport width in the overwhelming majority of cases, although I myself would include an xScroll = document.body.scrollLeft; statement (to be added to the above if (/\d+/.test(document.body.scrollTop)) conditional) to be on the safe side.

The arrayPageScroll return

The yScroll value is assigned to the second element of an arrayPageScroll array (i.e., yScroll becomes arrayPageScroll[1]), which is subsequently returned to the var arrayPageScroll = getPageScroll( ); statement in the showLightbox( ) function. There's no need to put yScroll in an array if we don't address horizontal scrolling; rather, we can directly return yScroll itself to the showLightbox( ) function. Conversely, if we take horizontal scrolling into account, then yes, an array that holds both xScroll and yScroll values is what we'll want to return.

In sum, here's how I would code the getPageScroll( ) function:

function getPageScroll( ) {
    var xScroll, yScroll, arrayPageScroll;
    if (/\d+/.test(document.body.scrollTop)) {
        xScroll = document.body.scrollLeft;
        yScroll = document.body.scrollTop; }
    arrayPageScroll = new Array(xScroll, yScroll);
    return arrayPageScroll; }


The preceding function leaves Netscape 6 users out in the cold, but that's life.

We'll discuss the getPageSize( ) function in the following entry.

reptile7

Wednesday, January 11, 2012
 
Lightbox III
Blog Entry #238

We're not quite done with the lightbox image viewer's lightbox.js initLightbox( ) function yet: below we look at its z-index assignments and Image.onload conditionalization code.

The z-index follies

The overlay div, the loading.gif image (loadingImage img), the lightbox div, and the close.gif image (closeButton img) are all given z-index settings:

objOverlay.style.zIndex = "90"; ...
objLoadingImage.style.zIndex = "150"; ...
objLightbox.style.zIndex = "100"; ...
objCloseButton.style.zIndex = "200";


The CSS z-index property allows for otherwise-overlapping content to be stacked along a "z-axis" that runs perpendicular to the document content area and whose positive direction points toward the user. The z-index property indirectly applies to all elements: more specifically, it applies to positioned elements, and the CSS position property applies to all elements. The aforelisted viewer elements are all absolutely positioned.

#overlay, #loadingImage, #lightbox, #closeButton { position: absolute; }

Other references
• It was Microsoft that in IE 4 introduced z-index as an (almost-)element-wide property; Microsoft's z-index page is here.
• Much more narrowly, Netscape at about the same time implemented z-index as an attribute of the layer element - I am not inclined to link to this material as it pertains to Netscape 4.x and only to Netscape 4.x. Mozilla's current z-index page is here; also, Mozilla has written up an "Understanding CSS z-index" tutorial that I very much encourage you to read through.

Getting back to the above z-index assignments, do we really need them? Is it necessary to proactively stack the loading.gif image and the lightbox div on the overlay div? Is it necessary to stack the close.gif image on the lightbox div? In a word, the answer to these questions is "No".

Let's start at the beginning. For the purpose of painting a document onto the canvas, the browser assigns each document element to a "stacking context". The overlay div and the lightbox div are siblings and do not have any positioned ancestors with non-auto z-index settings; consequently, they both belong to the "root stacking context" that is generated by the document's html element. By virtue of their own absolute positioning and non-auto z-index settings, each of these elements establishes a new stacking context that is atomic with respect to (independent of) the root stacking context.

The loading.gif image - more precisely, the loadingImage img element that holds it - is part of the stacking context established by the overlay div; its z-index setting is only meaningful relative to the z-index settings of other elements that are also part of that stacking context and not to the z-index settings of elements that are part of other stacking contexts, including the root stacking context. Be that as it may, the loading.gif image doesn't need a z-index for a fundamental reason: it doesn't overlap other content. (Indeed, the loading.gif image is the only renderable content in the overlay div.)

More formally, the aforelinked Layered presentation section of the CSS 2.1 Specification states:
Within each stacking context, the following layers are painted in back-to-front order:
(1) the background and borders of the element forming the stacking context;
(2) the child stacking contexts with negative stack levels (most negative first);
(3) the in-flow, non-inline-level, non-positioned descendants;
(4) the non-positioned floats;
(5) the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks;
(6) the child stacking contexts with stack level 0 and the positioned descendants with stack level 0;
(7) the child stacking contexts with positive stack levels (least positive first).
The overlay div's background, which is set by an
#overlay { background-image: url(overlay.png); }
rule in the lightbox.css style sheet, is in layer 1 and is painted first. The loadingImage img, whose
position: absolute; z-index: 150;
style establishes a child stacking context, is in layer 7 and is thus painted on top of the overlay div background. If we were to subtract the loadingImage img's z-index setting, then the loadingImage img would shift to layer 6 as it would then be a positioned descendant with stack level 0, and it would again be painted on top of the overlay div background.

The closeButton img element that holds the close.gif image is part of the stacking context established by the lightbox div, and its
top: 5px; right: 5px;
lightbox.css positioning causes it to overlap a 15px-by-15px square of the main lightbox image (it also overlaps a small part of the lightbox div's padding area). The lightbox.css style sheet gives the lightbox div the following style:

#lightbox { background-color: #eee; padding: 10px; border-bottom: 2px solid #666; border-right: 2px solid #666; }

Let's get painting, shall we? The lightbox div's background-color, border-bottom, and border-right are in layer 1. The lightboxImage img element that holds the main lightbox image is in layer 5 as it is an in-flow, inline-level, non-positioned descendant, and is painted on top of the lightbox div background. The closeButton img is in layer 7 and is painted on top of the lightboxImage img. If we were to subtract the closeButton img's z-index setting, then the closeButton img would shift to layer 6 and would still be rendered on top of both the lightboxImage img and the lightbox div background.

As for the lightbox div's remaining content, which is also part of its stacking context, the lightboxCaption div and the keyboardMsg div are both floated by the lightbox.css style sheet

#lightboxCaption { float: left; }
#keyboardMsg { float: right; }


which puts them in layer 4; they are painted on top of the lightbox div background but otherwise do not overlap other content.

Ah, but what about the relative stack levels of the overlay div and the lightbox div themselves? It is true that if the overlay div is given a z-index, then the lightbox div must be given a z-index whose value is greater than the overlay div z-index if we want the lightbox div to sit on the overlay div. The absolute z-index values of these elements are not important: the 90 and 100 values (vide supra) could be 1 and 2 or 500 and 1000 for that matter. However, the W3C notes, Boxes with the same stack level in a stacking context are stacked back-to-front according to document tree order - in general, this is why descendant elements are rendered on top of their ancestors - if we get rid of both z-indexes, then the lightbox div will be painted on top of the overlay div anyway because it follows the overlay div in the document source.

If we were to code the lightbox div as a child of the overlay div - that would make more sense, yes? - then in the absence of any z-index settings both divs would be in layer 6 of the root stacking context; the lightbox div would be further along the document tree than the overlay div and thus would again be painted on top of the overlay div.

In sum, the initLightbox( ) z-index assignments are all unnecessary and can be thrown out. All of the lightbox elements can be safely left in the root stacking context, whose default painting order is sufficient for our needs.

Img deployment

The initLightbox( ) function conditions the creation of the loadingImage img and its anchor element parent, and the insertion of these elements into the overlay div, on the availability of the loading.gif image. After the overlay div is created and inserted into the body element, the loading.gif image is loaded into RAM as the src of an imgPreloader Image object:

var imgPreloader = new Image( ); ...
imgPreloader.src = loadingImage;


Sandwiched between the preceding statements is a function expression that creates and deploys the loadingImage img and its link container if and when the imgPreloader Image has finished loading:

imgPreloader.onload = function( ) {
    var objLoadingImageLink = document.createElement("a"); ...
    objOverlay.appendChild(objLoadingImageLink);
    var objLoadingImage = document.createElement("img"); ...
    objLoadingImage.setAttribute("id", "loadingImage"); ...
    objLoadingImageLink.appendChild(objLoadingImage); ...
    return false; }


Execution of the post-function imgPreloader.src = loadingImage; statement dispatches a load event (I didn't know that - you learn something new every day!) and triggers the function.

After the loadingImage img is placed in the objLoadingImageLink link, the function expression sets imgPreloader.onload to an empty function:

imgPreloader.onload = function ( ) { };
/* Clear onLoad, as IE will flip out w/ animated gifs */


I have to say that I don't see the point of this statement given that the function expression is only executed once: when the lightbox.html page loads, and that's it. I don't know what sort of IE effect the author is trying to choke off here, but if truth be told, you can dispense with the loading animation image thing entirely if you use a thumbnail image interface and preload your main lightbox images into the thumbnail img placeholders as described in The HTML interface section of Blog Entry #236; speaking as a dial-up user, I don't see the loading.gif image at all when I do this.

The initLightbox( ) function similarly conditions the creation of the closeButton img, and its placement in the objLink link child of the lightbox div, on the availability of the close.gif image:

var imgPreloadCloseButton = new Image( );
imgPreloadCloseButton.onload = function ( ) {
    var objCloseButton = document.createElement("img"); ...
    objCloseButton.setAttribute("id", "closeButton"); ...
    objLink.appendChild(objCloseButton);
    return false; }
imgPreloadCloseButton.src = closeButton;


The imgPreloader.onload and imgPreloadCloseButton.onload function bodies both conclude with a return false; statement; these statements serve no purpose - there's nothing they prevent from happening - and can be thrown out.

We are now ready to display the lightbox image viewer by calling the lightbox.js showLightbox( ) function, which we'll dissect in the following entry.

reptile7

Sunday, January 01, 2012
 
Lightbox II
Blog Entry #237

Today we will begin in earnest a deconstruction of the lightbox.js script of the lightbox image viewer spotlighted by HTML Goodies' "How To Use the JavaScript Lightbox Image Viewer" tutorial. You may peruse the lightbox.js code in its entirety here.

When the lightbox.html page for the user has finished loading, the lightbox.js script's initLightbox( ) function is called per its assignment to window.onload by the script's addLoadEvent( ) function as discussed in the Pre-load section of the previous post. The initLightbox( ) function does two things:
(1) It registers the script's showLightbox( ) function with the rel="lightbox" anchor elements in the lightbox.html document.
(2) It codes, but does not display, most of the lightbox image viewer.

showLightbox( ) registration

The lightbox.js showLightbox( ) function, which does display the lightbox image viewer and which we'll get to later, is registered with the rel="lightbox" links by the following code:

function initLightbox( ) {
    if (!document.getElementsByTagName) { return; }
    var anchors = document.getElementsByTagName("a");
    for (var i = 0; i < anchors.length; i++) {
        var anchor = anchors[i];
        if (anchor.getAttribute("href") && (anchor.getAttribute("rel") == "lightbox")) {
            anchor.onclick = function ( ) { showLightbox(this); return false; } } } ...


We first check the browser's document.getElementsByTagName( ) support; browsers without that support are sent packing, i.e., they exit the initLightbox( ) function via a return statement. The getElementsByTagName( ) method goes back to Level 1 of the Core DOM, so unless you're using a really old browser (as in IE 4.x/Netscape 4.x or earlier) you should be good to go.

We next scoop up all of the lightbox.html anchor elements, whether or not they are associated with the lightbox image viewer; these elements are organized as an anchors array.

Finally, we use a for loop
(1) to identify the anchors members
(a) that have a href attribute not set to an empty string AND
(b) whose rel attribute is set to lightbox, and
(2) to assign a function ( ) { showLightbox(this); return false; } function expression to the onclick attribute of those anchors.
The return false; business cancels the HTML behavior of the anchors (going to the href URL when clicked) for users with JavaScript support.

We wouldn't need to screen for the href attribute if we were to instead work with the document.links[ ] collection. Moreover, the lightbox links are more appropriately grouped via the class attribute than via the rel attribute. If we give each lightbox link a class="lightboxLink" marker, then the above showLightbox( ) registration code can be rewritten as:

for (var i = 0; i < document.links.length; i++)
    if (document.links[i].className == "lightboxLink")
        document.links[i].onclick = function ( ) { showLightbox(this); return false; }


Before moving on...
Tobe in the tutorial comment thread asks:
I really do appreciate this wonderful image viewer script but then I would like my user to just hover over the image to produce a lightbox effect showing a larger image rather than clicking on it first. How can this be done?
Just replace onclick with onmouseover and you've got it, Tobe.

Viewer structure

The remainder of the initLightbox( ) function codes almost all of the lightbox image viewer's structure, most of its behavior, and some of its functional (layout-related) styles. Assuming that the loading.gif and close.gif images are available, here's the structure that we'll be creating:

<div id="overlay">
<a href="#">
<img src="loading.gif" id="loadingImage" />
</a>
</div>
<div id="lightbox">
<a href="#" title="Click to close">
<img src="close.gif" id="closeButton" />
<img id="lightboxImage" />
</a>
<div id="lightboxDetails">
<div id="lightboxCaption"></div>
<div id="keyboardMsg">press <kbd>x</kbd> to close</div>
</div>
</div>


There are ten elements in all; each element is created by a document.createElement( ) command:

var objOverlay = document.createElement("div"); ...
var objLoadingImageLink = document.createElement("a"); ...


The id, href, and title attributes are set via Element.setAttribute( ) commands whereas the img src attributes are set directly as properties of the calling Image objects:

objOverlay.setAttribute("id", "overlay"); ...
objLoadingImageLink.setAttribute("href", "#"); ...
objLoadingImage.src = loadingImage; ...
objLink.setAttribute("title", "Click to close"); ...


The setAttribute( ) method can sometimes cause problems for IE users - I don't know if this is true in the present case - it might be safer to set all of the attributes à la the src attribute:

objOverlay.id = "overlay"; ...
objLoadingImageLink.href = "#"; ...
objLink.title = "Click to close"; ...


The overlay div will be the viewer overlay (once we kit it out) and holds the loading.gif image; it is deployed as the first child of the body element by the following commands:

var objBody = document.getElementsByTagName("body").item(0); ...
objBody.insertBefore(objOverlay, objBody.firstChild);


When I first saw this, I said, "Huh?" Elements?? The HTML html element has a HEAD, BODY content model: a valid document cannot contain more than one body element. Alternatively, we can and should reference the body element with document.body, which was standardized in Level 1 of the HTML DOM.

The lightbox div holds the close.gif image, an img placeholder for the main image (whose src will be set later), a div container for an image caption (currently empty), and a div containing a press <kbd>x</kbd> to close 'keyboard message'; variabilized as objLightbox by its createElement( ) command, it is added to the body element as a sibling of the overlay div by:

objBody.insertBefore(objLightbox, objOverlay.nextSibling);

The remaining elements are put in place via Node.appendChild( ) commands:

objOverlay.appendChild(objLoadingImageLink); ...
objLoadingImageLink.appendChild(objLoadingImage); ...


Before moving on...
The W3C doesn't have much to say about the kbd element: Indicates text to be entered by the user - that's all that's there, folks.

Viewer behavior

The lightbox.js hideLightbox( ) function, which zeroes out the lightbox image viewer and which we'll get to later, is registered with the overlay div (objOverlay), the overlay div's anchor element child (objLoadingImageLink), and the lightbox div's anchor element child (variabilized as objLink by its createElement( ) command), more specifically, a function ( ) { hideLightbox( ); return false; } function expression is assigned to the onclick attribute of these elements:

objOverlay.onclick = function ( ) { hideLightbox( ); return false; } ...
objLoadingImageLink.onclick = function ( ) { hideLightbox( ); return false; } ...
objLink.onclick = function ( ) { hideLightbox( ); return false; }


The objLoadingImageLink assignment is redundant: even if we were to click directly on the loading.gif image, that click event would bubble up to the overlay div. And because the only purpose of the objLoadingImageLink link is to serve as a carrier for a hideLightbox( ) trigger, it follows that the link itself is redundant and can be thrown out. For that matter, the objLink link is also unnecessary as its hideLightbox( ) trigger and title attribute can be transferred to the lightbox div.

If the hideLightbox( ) registrations are vested in the overlay and lightbox divs, then they can be simplified to:

objOverlay.onclick = hideLightbox;
objLightbox.onclick = hideLightbox;


The div element doesn't have a default behavior and consequently doesn't need a function expression equipped with a return false; statement.

The press <kbd>x</kbd> to close behavior is set up by the showLightbox( ) function and we'll get to it when we discuss that function.

Viewer layout, part 1

The initLightbox( ) function effectively creates the following style sheet for the lightbox image viewer:

/* I refer you to the lightbox.js source for the actual statements; I'm writing them out as a style sheet as that'll take up less space. */
#overlay { display: none; position: absolute; top: 0px; left: 0px; z-index: 90; width: 100%; }
#loadingImage { position: absolute; z-index: 150; }
#lightbox { display: none; position: absolute; z-index: 100; }
#closeButton { position: absolute; z-index: 200; }
#lightboxCaption { display: none; }


The origin (upper-left-hand corner) of the overlay div is aligned with that of the document content area by the position: absolute; top: 0px; left: 0px; declaration set.

I initially suspected the overlay div's width: 100%; style to be unnecessary as the div element, like most block-level elements, ordinarily has an effective width of 100%: I was wrong. As an absolutely positioned, non-replaced element with a non-auto left value and an auto right value, the overlay div actually has a "shrink-to-fit" width*; in practice, that width will shrink to 0 when the loading.gif image is later removed from the div's "box" by virtue of its own absolute positioning that centers it in the viewport, or at least that's what happens on my computer, and so yes, we do need to give the overlay div a specific width.

*Regarding the
shrink-to-fit width = min(max(preferred minimum width, available width), preferred width)
formula, the "available width" is the width of the div's containing block (i.e., the width of the viewport, see below) whereas the "preferred minimum width" and the "preferred width" are both evidently 0 if the div doesn't contain any renderable content, and thus it's the "preferred width" that establishes the width of the div.

The overlay div's containing block is the "initial containing block", which has the dimensions of the viewport. If the width of the document exceeds that of the viewport, then the overlay div will not cover the part of the document that lies beyond the right edge of the viewport. This is an easily remediable problem, however, and we'll get it sorted out when we display the overlay div in the showLightbox( ) function.

(Relatedly, the initLightbox( ) function contains unnecessary calls to the lightbox.js getPageSize( ) and getPageScroll( ) functions:

var arrayPageSize = getPageSize( );
var arrayPageScroll = getPageScroll( );


These statements also appear in the showLightbox( ) function, which uses the arrayPageSize/arrayPageScroll returns to further lay out the overlay div, the loading.gif image, and the lightbox div; however, initLightbox( ) does nothing with the arrayPageSize/arrayPageScroll returns.)

There's no need to zero out the lightboxCaption div as its parent lightbox div is zeroed out.

The z-index stuff deserves its own section and we'll go through it next time.

reptile7


Powered by Blogger

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