Click to See Complete Forum and Search --> : loading one of two graphics


poobah_252
01-22-2003, 10:57 AM
I am trying to create an image in client HTML that displays one of two graphic files, depending on whether both are available. Specifically, if there is a non-English version of the graphic, I want to display that. But if not, I want to display the English version as a backup. The English version should always be available, but the script should react gracefully of course if / when it is not.

I have tried a couple of approaches, both of which worked reasonably well in my IE6 testing, but failed in Netscape 4.79.

The first approach was to declare an "onerror" handler in the "<img>" tag. The error handler loaded the English graphic when the non-English load failed.

The second approach (excerpt below), is to check whether the graphic loaded by checking the height / width of the loaded graphic to determine whether it exists or not. This unfortunately seems to be browser specific. It also seems to have the drawback that I think it is dependent on the graphic download speed whether it works on the initial page load or not. For bandwidth reasons, I'd prefer to only load the English graphic when the non-English is unavailable.

The main problem I'm encountering is that Netscape only briefly displays the graphic, sort of as a miniature thumbnail form, then quickly switches to a broken link.

Does anyone have a suggestion for a more robust, preferably less browser-dependent, approach to try?

Thanks, Bill
Wed, 22 Jan 2003, 11:57am EST

***** begin sample script *****
<html>

<head>
<title>test translated graphic</title>
<script type="text/javascript">

var translatedImage = new Image();
translatedImage.src = "spanish.gif";

var englishImage = new Image();
englishImage.src = "english.gif";

//
// Browser Detection
// - http://javascript.about.com/library/scripts/blbrowsercheck.htm
//
IE4plus = (document.all) ? true : false;

var testHeight;
var testWidth;

if (IE4plus) {
testHeight = 30;
testWidth = 28;
} else {
testHeight = 0;
testWidth = 0;
}

</script>
</head>


<body>
<hr />

<div>
<img name="test" />
</div>

<script type="text/javascript">
if ( ( testHeight != translatedImage.height) && ( testWidth != translatedImage.width ) ) {
document.images["test"].height = translatedImage.height;
document.images["test"].width = translatedImage.width;
document.images["test"].alt = translatedImage.src;
document.images["test"].src = translatedImage.src;
} else {
document.images["test"].height = englishImage.height;
document.images["test"].width = englishImage.width;
document.images["test"].alt = englishImage.src;
document.images["test"].src = englishImage.src;
}
</script>

gil davis
01-22-2003, 01:25 PM
You problem with NS 4 and the image size stems from the fact that once an image has been loaded, changing the SRC does not resize the image. To get the right size, you would have to use document.write() to change the image. Also, NS 4 will not reflow the rest of the page after the image. If the images are the same size, and you include the height and width in the HTML, you can avoid this phenomenon. However, because of this, you will have to use "browser-dependent" coding.

Having said that, how about this:

<head>
<script>
var bad = false;
var pic = new Image();
pic.onerror = function () {bad = true;}
pic.src="dontexist.gif";
</script>
</head>
<body>
Stuff above<br>
<script>
if(bad)
{document.write('<img src="cancel.gif">');}
else
{document.write('<img src="dontexist.gif">');}
</script>
<br>Stuff below
</body>

poobah_252
01-22-2003, 03:11 PM
Thanks for your suggestion, Gil. That looks simpler and more elegant than anything I had thought of yet. It works fine in IE6, and resolves the image size problem I was having in Netscape 4.79, but the English image won't load when I rename the Spanish. I don't know whether it might be a timing issue with the "onerror" handler not firing yet by the time the body script is executing, or something else. I'm going to try using a script debugger to see what's going on.

New code, adapted from yours:

<html>
<head>
<title>test translated graphic</title>
<script type="text/javascript">
var noTranslatedGraphic = false;
var pic = new Image();
pic.onerror = function () { noTranslatedGraphic = true; };
pic.src = "spanish.gif";
</script>
</head>
<body>
<hr />
<script type="text/javascript">
if (noTranslatedGraphic)
{ document.write('<img src="english.gif">'); }
else
{ document.write('<img src="spanish.gif">'); }
</script>
<hr />
</body>
</html>

poobah_252
01-22-2003, 03:13 PM
I did not clarify that very well. Both images load fine in IE, but only the Spanish in Netscape.

gil davis
01-22-2003, 03:27 PM
Mybe you're fighting the cache. If the image is in the cache, but not on the server, you get the cache (free of charge!).

poobah_252
01-22-2003, 04:35 PM
I believe your comment on the cache was the key, Gil. However, just clearing the cache was not sufficient to display the English properly once I had displayed the Spanish. I had to close and restart the browser (or at least that window) in order for Netscape to accurately realize that there is no Spanish graphic and it must display the English. Since the non-English graphics will either be available or not when the user first accesses the page in a session, and very unlikely to change during a session, this solution looks like it will work.

Thank you so much for your help! This challenge has been hanging over my head for some time now.

Best regards, Bill
Wed, 22 Jan 2003, 5:35pm EST

gil davis
01-23-2003, 05:54 AM
There is a memory cache as well as a disk cache. If you hold the shift key while clicking reload, the page should behave as if it were never loaded before (your mileage may vary, void where prohibited... ;)).

poobah_252
03-10-2003, 01:46 PM
I am noticing that this script works sometimes. It seems pretty reliable in Netscape 4.79, but in IE 5.5 or IE 6, sometimes the "onerror" handle apparently fails to execute.

I say "apparently", because when I try to determine more closely exactly what's going on, the page and graphic always loads correctly when I single-step through the javascript on the page using Microsoft Script Debugger.

What's happening is that sometimes in IE when the Spanish graphic does not exist, and "noTranslatedGraphic" should be set to "true" by the "onerror" function, it instead retains it default "false" state and the "spanish" "<img>" tag gets written out instead of the "english".

Anybody have any idea what might be going on, or what to check to help make this more robust in the IE environment?

Thanks, Bill
Mon, 10 March 2003, 2:45pm EST

gil davis
03-10-2003, 02:11 PM
The single-step-and-it-works indicates a race between the onerror and the document.write() part of the script. The system is probably still working on deciding what's up with the missing graphic. There is another parameter called "complete" that indicates when the system has finished the load procedure (good or bad), and there is also an onload event that occurs when the image has been loaded successfully. So it's almost like you need a three-state signal - "good load", "bad load", and "I haven't finished deciding yet".

var noTranslatedGraphic = "unknown";
...
pic.onerror = function () { noTranslatedGraphic = "bad"; };
pic.onload = function () { noTranslatedGraphic = "good"; };
...
if (noTranslatedGraphic != "unknown")
{if (noTranslatedGraphic == "good")
{document.write('<img src="english.gif">');}
else
{document.write('<img src="spanish.gif">');}
}
else
{// not sure what to do}
</script>

poobah_252
03-10-2003, 02:30 PM
Thanks for the quick reply, Gil!

I'll take a look at integrating the onload handler. Bill

poobah_252
03-10-2003, 02:45 PM
Upon noodling over the implications of your last post, Gil, it seems to me that I almost need to have a loop for each graphic on the page until either the onload or the onerror handler has fired for that graphic, so I know definitively that I have waited long enough (but no longer than necessary for a given bandwidth connection) to correctly determine the availability of the translated graphic.

Does that sound feasible, without significant risk of an infinite loop?

poobah_252
03-10-2003, 02:54 PM
Here's a first cut at implementing this approach:

var noTranslatedGraphic = "unknown";
...
pic.onerror = function () { noTranslatedGraphic = "bad"; };
pic.onload = function () { noTranslatedGraphic = "good"; };
...
while (noTranslatedGraphic == "unknown") {}
if (noTranslatedGraphic == "good")
{document.write('<img src="english.gif">');}
else
{document.write('<img src="spanish.gif">');}

poobah_252
03-10-2003, 03:27 PM
I tested this new approach in both NS 4.79 and IE 6. The preliminary result is that NS continues to perform just as expected -- quickly loading the English graphic. IE acts like the page load is stuck for maybe a minute or so, then finally pops up a warning box which reads:

"A script on this page is causing Internet Explorer to run slowly. If it continues to run, your computer may become unresponsive. Do you want to abort the script? { 'Yes' and 'No' buttons }"

My preliminary conclusion is that IE is firing neither the "onload" nor the "onerror" handler in a reasonable period of time when I try to load the nonexistent translated graphic file (I have a T1 link with pageload bandwidth on the order of 1,000 Kbps plus).

I wonder if this might be a known problem with IE, or if I should look further for some lack of robustness in my approach.

poobah_252
03-10-2003, 03:42 PM
Once again, I single-stepped this script in the Microsoft Script Debugger and it runs fine in that environment, correctly loading both handlers, firing the onerror handler, skipping the "while" loop body, and writing out the "IMG" tag with the English path.

I reloaded the page again and told the script debugger to execute full-speed (F5), and it hangs IE until the "abort script" popup appears, just as when I ran without the debugger.

Following is the snippet of code copied / pasted from my test page.

<html>

<head>
<script type="text/javascript">
var noTranslated_01c00387 = "unknown";
var testLoad_01c00387 = new Image();
testLoad_01c00387.onerror = function () { noTranslated_01c00387 = "true"; };
testLoad_01c00387.onload = function () { noTranslated_01c00387 = "false"; };
testLoad_01c00387.src = "/rtgraphics/spanish/service/01/c/01c00387.gif";
</script>
</head>

<body>
<script type="text/javascript">
while( "unknown" == noTranslated_01c00387 ) {}
if ( "true" == noTranslated_01c00387 )
{ document.write( "<img src='/rtgraphics/english/service/01/c/01c00387.gif'>" ); }
else
{ document.write( "<img src='/rtgraphics/spanish/service/01/c/01c00387.gif'>" ); }
</script>
</body>
</html>

gil davis
03-10-2003, 04:00 PM
Originally posted by poobah_252
while (noTranslatedGraphic == "unknown") {}I've never seen this work successfully. The browser usually blew it's stack.

You won't be able to generalize timeout lengths. I think there are too many variables, especially if you have more than one image you are waiting on.

Dave Clark has written some things about image loading and errors. Perhaps you can search the forum, or he might chime in by himself.

Alternately, maybe you can use a blank image as a place holder:pic.onload = function () {temp = "english.gif";}
pic.onerror = function () {temp = "spanish.gif";}
window.onload = function () {document.i1.src = temp;}
...
<img name="i1" src="blank.gif">At least it eliminates the document.write().

poobah_252
03-11-2003, 12:43 PM
You're right about the "while" loop, Gil. It caused IE to hang until it popped up a box offering to abort the script after about a minute.

I did a search on "Dave Clark" and "image load errors" and found one thread at:

http://forums.webdeveloper.com/showthread.php?s=&threadid=2372

I tried dropping the loop and doing a single reload in the body if the header loads were inconclusive. This seems to typically work in IE the second time I load the page, but I have not thought yet of a reliable way to trigger a single reload of a page if the first load was not conclusive.

Following is my latest attempt, with some instrumentation added to see what's happening without using the script debugger.

<html>
<head>
<script type="text/javascript">
var noTranslated_01c00387 = "unknown";
var testLoad_01c00387 = new Image();

testLoad_01c00387.onerror = function () { noTranslated_01c00387 = "true"; };
testLoad_01c00387.onload = function () { noTranslated_01c00387 = "false"; };
testLoad_01c00387.src = "/rtgraphics/spanish/service/01/c/01c00387.gif";
if ( "unknown" == noTranslated_01c00387 ) { testLoad_01c00387.src = "/rtgraphics/spanish/service/01/c/01c00387.gif"; }
</script>
</head>

<body>
<script type="text/javascript">
if( "unknown" == noTranslated_01c00387 ) {
testLoad_01c00387.src = "/rtgraphics/spanish/service/01/c/01c00387.gif";
document.write( "<br/><br/><p>{ Retrying to see whether translated graphic exists. }</p><br/><br/>" ); }
if ( "false" == noTranslated_01c00387 )
{ document.write( "<img src='/rtgraphics/spanish/service/01/c/01c00387.gif'>" ); }
else
{ document.write( "<img src='/rtgraphics/english/service/01/c/01c00387.gif'>" ); }
if ( "true" == noTranslated_01c00387 )
{ document.write( "<br/><br/><p>{ Translated version of graphic does not exist. }</p><br/><br/>" ); }
if ( "unknown" == noTranslated_01c00387 )
{ document.write( "<br/><br/><p>{ Unable to determine whether non-English version exists. }</p><br/><br/>" ); }
</script>
</body>
</html>

poobah_252
03-11-2003, 05:14 PM
Eureka! A possible solution is found.

I have yet to test how well this scales to more than one graphic per page, but it seems adequately robust (if a little brute force and ugly) in IE 6 and 4, as well as NS 4.79.

I set a flag when loading the page, assuming that all of the translated graphic file checks will be definitive. If any are not, then I reload the page and try again. Assuming that any graphic successfully found on one pass is quickly available in cache on the next pass, I'm hoping that this will scale okay to multiple graphics too.

Thanks for all of the help and ideas to get to this point.

<html>
<head>
<script type="text/javascript">
var allTranslatedGraphicChecksSuccessful = true;

function recheckTranslatedGraphicsIfNeeded() {
if (!allTranslatedGraphicChecksSuccessful) {
window.location.reload();
}
}

var noTranslated_01c00387 = "unknown";
var testLoad_01c00387 = new Image();
testLoad_01c00387.onerror = function () { noTranslated_01c00387 = "true"; };
testLoad_01c00387.onload = function () { noTranslated_01c00387 = "false"; };
testLoad_01c00387.src = "/rtgraphics/spanish/service/01/c/01c00387.gif";
if ( "unknown" == noTranslated_01c00387 ) { testLoad_01c00387.src = "/rtgraphics/spanish/service/01/c/01c00387.gif"; }
</script>
</head>

<body onload="recheckTranslatedGraphicsIfNeeded()">
<script type="text/javascript">

if( "unknown" == noTranslated_01c00387 ) {
testLoad_01c00387.src = "/rtgraphics/spanish/service/01/c/01c00387.gif"; }

if ( "false" == noTranslated_01c00387 ) {
document.write( "<img src='/rtgraphics/spanish/service/01/c/01c00387.gif'>" ); }
if ( "true" == noTranslated_01c00387 ) {
document.write( "<img src='/rtgraphics/english/service/01/c/01c00387.gif'>" ); }
if ( "unknown" == noTranslated_01c00387 ) {
allTranslatedGraphicChecksSuccessful = false; }

</script>
</body>
</html>