Thursday, February 02, 2012
Lightbox V
Blog Entry #240
The getPageSize( ) function of the lightbox image viewer's lightbox.js script determines the width and height of the viewport and the lightbox.html page: the lightbox.js showLightbox( ) function will use this information to
(a) center the loading.gif image and the lightbox div in the viewport, and
(b) set the height of the overlay div.
Viewport dimensions
The getPageSize( ) function can be roughly divided into a viewport part and a page part; the viewport part is more straightforward so we'll go through it first:
function getPageSize( ) {
var windowWidth, windowHeight;
if (self.innerHeight) { // all except Explorer
windowWidth = self.innerWidth;
windowHeight = self.innerHeight; }
else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
windowWidth = document.documentElement.clientWidth;
windowHeight = document.documentElement.clientHeight; }
else if (document.body) { // other Explorers
windowWidth = document.body.clientWidth;
windowHeight = document.body.clientHeight; }
The preceding three-clause conditional was taken verbatim from the QuirksMode "Viewport properties" page that provided the lightbox.js getPageScroll( ) code discussed in the previous entry (OK, Lokesh respectively uses windowWidth and windowHeight in place of the original x and y variables but otherwise the code is identical).
To get the viewport's dimensions, we can use either the clientWidth/clientHeight or innerWidth/innerHeight property pair; the former is the better choice as the latter will also include the width/height of adjacent scrollbars, if present (see the image below).
• Introduced by Microsoft for IE 4, clientWidth and clientHeight are today supported by all of the major browsers. These properties apply to most elements and the W3C will be adding them to the Core DOM's Element interface.
• Complementarily, innerWidth/innerHeight are classical JavaScript properties that date to JavaScript 1.2, and apply to the window object; they are today supported by the most recent versions of all of the major browsers, although they are not supported by pre-IE 9 versions of Internet Explorer. The W3C will be adding innerWidth/innerHeight to an HTML5 Window interface.
(This image appears courtesy of the Mozilla Developer Network and its window.open( ) page.)
Upon consulting Microsoft's clientWidth/clientHeight pages, one would never conclude that these properties return the width/height of the viewport when applied to the body or html element; for example, the definition of these properties reads,
Retrieves the width/height of the object including padding, but not including margin, border, or scroll bar.Nevertheless, I find that
document.body.clientHeight
and window.innerHeight
give identical returns in the absence of a horizontal scrollbar for all of the browsers on my computer that support both clientHeight and innerHeight, regardless of whether the document height does or does not exceed the viewport height. Moreover, screenshot measurements confirm that document.body.clientHeight
for a height: 500px; margin: 20px; border: 5px solid red;
body element does indeed pick up the margin/border y-axis pixels.As in the getPageScroll( ) function, the above
document.documentElement
code flags IE 6 running in strict mode. An argument can be made that we shouldn't write off IE 6 users - according to Microsoft's "Internet Explorer 6 Countdown" page, 25.2% 0.66% of China is still using IE 6, and I'm sure that adds up to a lot (millions?) of people - my preferred way of accommodating them is not via document.documentElement
statements but to place a document type declaration preceded by a <!-- Quirks mode trigger -->
comment at the top of the lightbox.html document and then route them all through the else if (document.body)
clause.Excepting IE 5.2.3, the OS X GUI browsers on my computer all support clientHeight for the html element, but in this case
document.documentElement.clientHeight
really does read the content+padding height of the html element (you can actually set a CSS margin and border for the html element so as to verify this) rather than the height of the viewport. HOLD IT: I have just found out that document.body.clientHeight
gives the content+padding height of the body element with these browsers when a document type declaration is placed at the top of the lightbox.html document; this effect is not overridden by preceding the declaration with a comment or XML prolog. So it looks like we're gonna have to be running in quirks mode to get the viewport dimensions with clientWidth/clientHeight anyway.On Microsoft's clientWidth page, commenter Thomas Lee warns that
document.documentElement.clientWidth
and document.body.clientWidth
return 0 in some cases. On my computer,
document.documentElement.clientWidth
does give 0 with Netscape 7 but otherwise these expressions return undefined with browsers that do not support them. In the absence of further information, I am inclined to not keep the if (self.innerHeight)
clause, and to reduce the viewport part of the getPageSize( ) function to:if (document.body.clientHeight) {
var windowWidth = document.body.clientWidth;
var windowHeight = document.body.clientHeight; }
Page dimensions
Here's the getPageSize( ) function's page part in its entirety:
var xScroll, yScroll;
if (window.innerHeight && window.scrollMaxY) {
xScroll = document.body.scrollWidth;
yScroll = window.innerHeight + window.scrollMaxY; }
else if (document.body.scrollHeight > document.body.offsetHeight) { // all but Explorer Mac
xScroll = document.body.scrollWidth;
yScroll = document.body.scrollHeight; }
else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
xScroll = document.body.offsetWidth;
yScroll = document.body.offsetHeight; }
// for small pages with total height less than height of the viewport
if (yScroll < windowHeight) { pageHeight = windowHeight; }
else { pageHeight = yScroll; }
// for small pages with total width less than width of the viewport
if (xScroll < windowWidth) { pageWidth = windowWidth; }
else { pageWidth = xScroll; }
Classical JavaScript equipped the document object with width and height properties that respectively return the width and height in pixels of a document; neither Microsoft nor the W3C picked up these properties, however. (Some modern browsers (e.g., Chrome) support document.width and document.height, but IE and Firefox don't, so never mind, eh?)
Microsoft implemented in IE 4* an applies-to-most-elements scrollWidth/scrollHeight property pair that
retrieves the scrolling width/height of the object. The Remarks sections of Microsoft's scrollWidth/scrollHeight pages suggest that these properties measure the dimensions of an object's content box. In practice on my computer,
document.body.scrollWidth
and document.body.scrollHeight
give(1) the content+padding+border body dimensions with Firefox/Camino and Opera when running in strict mode, and
(2) the entire width/length of the document content area with Firefox/Camino and Opera when running in quirks mode and with Chrome/Safari regardless of mode: every last pixel of 'canvas real estate' is measured under these conditions, even if the document is horizontally/vertically smaller than the viewport, even if I give the html element a border and margin; adjacent scrollbars are not included.
So once again, we'll need to be running in quirks mode to get what we want.
*irt.org reports that scrollWidth/scrollHeight go back to IE 5; I find that IE 4.5 buggily supports them.
The scrollWidth/scrollHeight properties today have widespread support - like clientWidth/clientHeight, they are on track to be added to the Core DOM's Element interface - and a
if (document.body.scrollHeight) {
var pageWidth = document.body.scrollWidth;
var pageHeight = document.body.scrollHeight; }
conditional is all we really need for the page part of the getPageSize( ) function. But as you can see, we've got some other stuff up there.
After declaring the variables xScroll and yScroll (names suitable for the getPageScroll( ) function but not the getPageSize( ) function), getPageSize( ) begins its series of conditionals with an if clause that is meant to flag Firefox users by testing support for the scrollMaxY property of the window object. The
window.scrollMaxY
expression returns the maximum number of pixels that the document can be scrolled vertically; it is relevant to documents whose heights are larger than the viewport height and effectively measures the beyond-the-viewport part of the document height. On my computer,
window.scrollMaxY
is supported by Firefox, Camino, and Netscape 9.The
window.innerHeight
and window.scrollMaxY
expressions return undefined for browsers that don't support them. For browsers that support window.innerHeight
but not window.scrollMaxY
(Chrome/Safari, IE 9+, Opera), the window.innerHeight && window.scrollMaxY
if condition returns undefined because the && operator returns its second operand if its first operand can be converted to true; the undefined return converts to false in a logical context and consequently these browsers skip over the if clause body.(The
window.innerHeight && window.scrollMaxY
condition also returns undefined, and converts to false, for the pre-IE 9 versions of Internet Explorer that support neither window.innerHeight
nor window.scrollMaxY
because the && operator returns its first operand if it can be converted to false.)If the document height is smaller than the viewport height (as is the case at the "Lightbox" tutorial's isolated demo page), then
window.scrollMaxY
will return 0 for browsers that support it; these browsers will also skip over the if clause body in this case because window.innerHeight && window.scrollMaxY
will return 0, which also converts to false in a logical context.If the document height is larger than the viewport height, then
window.innerHeight && window.scrollMaxY
will convert to true for window.scrollMaxY
-supporting browsers; in this case, the if clause assigns document.body.scrollWidth
to xScroll and window.innerHeight + window.scrollMaxY
to yScroll. (Why isn't window.innerWidth + window.scrollMaxX
assigned to xScroll? Your guess is as good as mine.) However, the Notes section of Mozilla's scrollMaxY page states:Do not use this property to get the total document height, which is not equivalent to window.innerHeight + window.scrollMaxY, because window.innerHeight includes the [height] of any visible horizontal scrollbar, thus the result would exceed the total document height by the [height] of any visible horizontal scrollbar. Instead use document.body.scrollHeight.So there you have it, straight from the horse's mouth:
document.body.scrollHeight
is what we should be using to get the height of the page.Lokesh credits the if/scrollMaxY clause to Eric Iles (a.k.a. pHaez); the subsequent else if and else clauses come from the QuirksMode "Viewport properties" page. The else if clause respectively assigns
document.body.scrollWidth
and document.body.scrollHeight
to xScroll and yScroll, but conditions those assignments on the body element's scrollHeight being larger than its offsetHeight. For its part, the else clause respectively assigns document.body.offsetWidth
and document.body.offsetHeight
to xScroll and yScroll.Introduced by Microsoft for IE 4, the applies-to-most-elements offsetWidth and offsetHeight properties
retrieve the width/height of the object relative to the layout or coordinate parent, as specified by the offsetParent property. (The W3C will be adding offsetWidth/offsetHeight to the HTML DOM's HTMLElement interface.) The lightbox.html body element is not offset with respect to its html element parent, and document.body.offsetParent appropriately returns null for all of the browsers on my computer that support the offset- properties, but at the same time the body element's
layout or coordinate parentis still the html element, and
document.body.offsetWidth
and document.body.offsetHeight
in practice give non-undefined, integer returns with these browsers, more specifically, they return the content+padding+border body dimensions in all cases, regardless of mode, excepting IE 4.x-5.x, for which the else clause is intended.It's not really worth it for us to discuss how IE 4.x and 5.x interpret
document.body.offsetWidth
and document.body.offsetHeight
- there shouldn't be anyone out there using these browsers!** (Speaking as someone who was an IE 5.1.6 user back in the day, I can vouch that you'd experience major difficulties surfing today's Web with IE 4.x-5.x.) So let me just say that for modern browsers, the document.body.offsetWidth
/document.body.offsetHeight
returns give the page dimensions if(a) the document width/height exceeds the viewport width/height AND
(b) the body margin is set to 0,
but otherwise fall short of what we want. Lose the offset- code, keep the scroll- code.
**IE 5.2.3 performs very poorly at the aforelinked tutorial demo, although most of its problems with the original lightbox image viewer code are solvable.
The getPageSize( ) page part concludes with two if...else statements that are meant to assign the viewport height/width to pageHeight and pageWidth variables if the document height/width is smaller than the viewport height/width. Here's how these statements shake out with Firefox/Camino, Chrome/Safari, and Opera (the following should be true for IE 9+ as well) if we stick with the original getPageSize( ) code:
Quirks mode
If scrollbars are present, then the if clauses are operative: windowHeight/windowWidth (the innerHeight/innerWidth returns) are greater than yScroll/xScroll (the scrollHeight/scrollWidth returns) and are therefore assigned to pageHeight/pageWidth. If no scrollbars are present, then the else clauses are operative: the scrollHeight/scrollWidth returns are assigned to pageHeight/pageWidth, as they should be.
Strict mode
Whether or not scrollbars are present, the if clauses are operative: windowHeight/windowWidth are greater than yScroll/xScroll and are assigned to pageHeight/pageWidth.
So regardless of mode, if a vertical or horizontal scrollbar is present, then its width/height will be included in the pageWidth/pageHeight value, and we don't want that; this situation can be avoided by assigning the scrollHeight/scrollWidth returns to pageHeight/pageWidth (vide supra) in quirks mode.
As for IE 6-8, my best guess is that the else clauses will be operative, i.e., the scrollHeight/scrollWidth returns will be assigned to pageHeight/pageWidth, which should be OK in quirks mode but may miss some of the page in strict mode, depending on how faithfully IE 6-8 adhere to Microsoft's scrollHeight/scrollWidth/clientHeight/clientWidth specs.
What a mess all of this is, huh? Here is the take-home summary:
(1) Get the viewport dimensions via the clientWidth/clientHeight property pair and
(2) get the page dimensions via the scrollWidth/scrollHeight property pair
in quirks mode in both cases. If desired, validate your lightbox.html page with a document type declaration in place, and then subtract the declaration before you post the page.
Finally, the getPageSize( ) function respectively assigns pageWidth, pageHeight, windowWidth, and windowHeight to the first four elements of an arrayPageSize array, which is subsequently returned to the
var arrayPageSize = getPageSize( );
statement in the showLightbox( ) function.arrayPageSize = new Array(pageWidth, pageHeight, windowWidth, windowHeight);
return arrayPageSize;
We'll discuss the showLightbox( ) function's use of the getPageScroll( )/getPageSize( ) returns in the next entry.
reptile7
Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)