reptile7's JavaScript blog
Monday, March 19, 2007
 
Dimensions Delayed and DOM-Determined
Blog Entry #70

Back to the Script Tips #45-48 Script, whose first script element we dissected in the previous entry. So, let's suppose that the user correctly enters angel.jpg into the prompt( ) box and clicks "OK". When the page loads, the user sees the angel.jpg image and, per the HTML at the bottom of the Script Tips #45-48 Script, a "Resize It" heading plus two one-row tables for manipulating the image. The first two cells of each table display respectively the intrinsic height and width of the image - or do they?

The intrinsic dimensions of the angel.jpg image are:
Height=258 pixels, Width=199 pixels
On my iMac, here's what happens when I access the script demo page: for a first-time visit - if the angel.jpg image is not cached - Netscape 7.02 displays Height=24, Width=24 in the tables at the bottom of the page; similarly, MSIE 5.1.6 displays Height=26, Width=26. Upon reloading the page (and going through the prompt( ) dialog a second time), Netscape always displays the intrinsic dimensions, whereas with MSIE I still see Height=26, Width=26.

So what's going on? Watching the page load, it is clear that when either browser hits the

else document.write("<img name='thepic' src=" + path + ">");

command line that posts the image, it doesn't just stop while angel.jpg loads, but continues to move through the document source. Specifically, the browser proceeds to the second script element and executes the followng two statements:

var high = document.thepic.height;
var wide = document.thepic.width;

If angel.jpg has not loaded, then high and wide return the default dimensions of an empty <img /> placeholder, which are ≅25×25 in my case (Joe reports 40×40 on his machine in Script Tip #45). Subsequently, high and wide are picked up by the first two cells of each table:

<td align="center">Height:<br><script language="javascript">document.write("<b>"+ high +"</b>")</script></td>
<td align="center">Width:<br><script language="javascript">document.write("<b>"+ wide +"</b>")</script></td>

The script uses the wide/high and high/wide ratios to resize the image, so it is important that the high and wide values are correct. (Conversely, the 25×25 or 40×40 dimensions wouldn't be an issue if angel.jpg were a square image, but it isn't.) Obviously, then, we need to ensure that angel.jpg has completely loaded before high and wide are set. Perhaps the easiest way to do this - in theory, at least - is to preload the angel.jpg image:

// Before the prompt( ) command in the first script element, insert:
thepic = new Image( );
thepic.src = "angel.jpg";

In practice, I find that these lines of code make no difference when using Netscape, which continues to display Height=24, Width=24 (MSIE does show the intrinsic dimensions); moreover, preloading goes against the idea of spontaneously choosing an image and working with it. Well, what about putting the high and wide declarations in a function that is delayed by a window.setTimeout( ) command? This is a step in the right direction, but again, a setTimeout( ) command will not stall the browser, which will barrel on to the table code and promptly throw "'high' is undefined" and "'wide' is undefined" runtime errors.

Optimally, we would write high, wide, and the contents of the table cells that contain them in a single, time-delayed function, and it turns out that this isn't difficult to do, although it will require us to make use of some specialized DOM tools. Outlined below are two cross-browser methods for reliably and reproducibly generating correct values for high and wide.

(1) Method #1 employs the DOM innerHTML property, which sets or gets all of the markup and content within a given element, quoting Mozilla. The innerHTML property was originally developed by Microsoft as a proprietary MSIE extension but is now supported by other browsers; however, it's not listed in the "Attributes" section of the DOM Level 3 Core Specification's Element Interface.

Let's begin by recoding the first two cells of the two tables as follows:

Table #1:
<td align="center" id="cell0"></td>
<td align="center" id="cell1"></td>
Table #2:
<td align="center" id="cell6"></td>
<td align="center" id="cell7"></td>
<!--We'll address the align="center" attribute, and other style features of the Script Tips #45-48 Script, in a subsequent post.-->

It just occurred to me that none of the scripts previously discussed on this blog has featured an HTML table, so let me give you a couple of references here:
• For the basics of HTML table creation, check out HTML Goodies' "So, You Want A Table, Huh?" tutorial.
• The W3C discusses HTML tables in Chapter 11 of the HTML 4.01 Specification.

Next, we remove the high and wide declarations in the second script element and, building on the code given in the previous post, put the following function in the first script element and after the imageFileDialog( ) and badImage( ) functions:

var high; var wide;
function highwide( ) {
high = document.thepic.height;
wide = document.thepic.width;
// In the following statements, <br /> cannot be replaced with \n or &#10;.
document.getElementById("cell0").innerHTML = "Height:<br /><b>" + high + "</b>";
document.getElementById("cell1").innerHTML = "Width:<br /><b>" + wide + "</b>";
document.getElementById("cell6").innerHTML = "Height:<br /><b>" + high + "</b>";
document.getElementById("cell7").innerHTML = "Width:<br /><b>" + wide + "</b>"; }

Finally, we can call the highwide( ) function after, say, a 500-millisecond time delay via an img element onload attribute:

<img name="thepic" src="" alt="Here's where the image should be." onload="window.setTimeout('highwide( );',500);" />

We've heretofore always used the onLoad event handler with the body element, but we briefly noted here in Blog Entry #10 that it can also be used with the img element. In its Description of the onLoad event handler, Netscape notes, For images, the onLoad event handler indicates the script to execute when an image is displayed. Do not confuse displaying an image with loading an image.

(2) Method #2 is more in sync with the script's original design. Instead of writing the entire contents of the table cells, we will again add the values of high and wide to the cells' preexisting Height/Width:<br><b>x</b> content; this time, however, we'll write high and wide not via separate JavaScript scripts but as new DOM "text nodes." Consider the first cell in the first table:

<td align="center">Height:<br><script language="javascript">document.write("<b>" + high + "</b>")</script></td>

With an eye on separating structure and presentation at a later point, let's replace the script element with a span element as follows:

<td align="center">Height:<br /><span id="span0" style="font-weight:bold;"></span></td>

We can now append high as a text node "child" to the span element via the following two commands:

var highValue = document.createTextNode(high);
document.getElementById("span0").appendChild(highValue);

Or one command, if you prefer:

document.getElementById("span0").appendChild(document.createTextNode(high));

Mozilla's DOM Reference has a page here for the createTextNode( ) method of the document object and a page here for the appendChild( ) method. In the DOM Level 3 Core Specification, the createTextNode( ) method is, appropriately, listed in the Document Interface, whereas the appendChild( ) method is listed in the Node Interface (and not in the Element Interface).

Let's put it all together à la Method #1 above. We begin by recoding the first two cells of the two tables:

Table #1:
<td align="center">Height:<br /><span id="span0" style="font-weight:bold;"></span></td>
<td align="center">Width:<br /><span id="span1" style="font-weight:bold;"></span></td>
Table #2:
<td align="center">Height:<br /><span id="span6" style="font-weight:bold;"></span></td>
<td align="center">Width:<br /><span id="span7" style="font-weight:bold;"></span></td>

We again remove the high and wide declarations in the second script element and then put a retooled highwide( ) function in the first script element:

var high; var wide;
function highwide( ) {
high = document.thepic.height;
wide = document.thepic.width;
document.getElementById("span0").appendChild(document.createTextNode(high));
document.getElementById("span1").appendChild(document.createTextNode(wide));
document.getElementById("span6").appendChild(document.createTextNode(high));
document.getElementById("span7").appendChild(document.createTextNode(wide)); }

Method #1's

<img name="thepic" src="" alt="Here's where the image should be." onload="window.setTimeout('highwide( );',500);" />

highwide( ) function call can be used 'verbatim' for Method #2.

And that wraps up today's foray into the DOM. In the following post, we'll finally get around to resizing/redisplaying the angel.jpg image, and also perhaps revamp the script a bit.

reptile7

Labels: , , ,


Comments: Post a Comment

<< Home

Powered by Blogger

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