views:

9847

answers:

7

I have an img tag in my webapp that uses the onload handler to resize the image:

<img onLoad="SizeImage(this);" src="foo" >

This works fine in Firefox 3, but fails in IE7 because the image object being passed to the SizeImage() function has a width and height of 0 for some reason -- maybe IE calls the function before it finishes loading?. In researching this, I have discovered that other people have had this same problem with IE. I have also discovered that this isn't valid HTML 4. This is our doctype, so I don't know if it's valid or not:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;

Is there a reasonable solution for resizing an image as it is loaded, preferably one that is standards-compliant? The image is being used for the user to upload a photo of themselves, which can be nearly any size, and we want to display it at a maximum of 150x150. If your solution is to resize the image server-side on upload, I know that is the correct solution, but I am forbidden from implementing it :( It must be done client side, and it must be done on display.

Thanks.

Edit: Due to the structure of our app, it is impractical (bordering on impossible) to run this script in the document's onload. I can only reasonably edit the image tag and the code near it (for instance I could add a <script> right below it). Also, we already have Prototype and EXT JS libraries... management would prefer to not have to add another (some answers have suggested jQuery). If this can be solved using those frameworks, that would be great.

Edit 2: Unfortunately, we must support Firefox 3, IE 6 and IE 7. It is desirable to support all Webkit-based browsers as well, but as our site doesn't currently support them, we can tolerate solutions that only work in the Big 3.

+6  A: 

IE7 is trying to resize the image before the DOM tree is fully rendered. You need to run it on document.onload... you'll just need to make sure your function can handle being passed a reference to the element that isn't "this."

Alternatively... and I hope this isn't a flameable offense... jQuery makes stuff like this really, really easy.

EDIT in response to EDIT 1:

You can put document.onload(runFunction); in any script tag, anywhere in the body. it will still wait until the document is loaded to run the function.

Steve Paulo
usually trying to modify a portion of the dom before its loaded will cause the dreaded "operation aborted" message.
StingyJack
+2  A: 

The way I would do it is to use jQuery to do something like:

$(document).load(function(){
    // applies to all images, could be replaced 
    //by img.resize to resize all images with class="resize"
    $('img').each(function(){
        // sizing code here
    });
});

But I'm no javascript expert ;)

Ross
+4  A: 

If you don't have to support IE 6, you can just use this CSS.

yourImageSelector {
    max-width: 150px;
    max-height: 150px;
}
eyelidlessness
That's a really good idea... I didn't even know that was an available CSS option. Unfortunately, we need to support IE6. I updated the question to mention that.
rmeador
Here is how to support IE6: <!--[if lt IE 7]> <style> yourImageSelector { width: expression(this.width > 150 ? 150: true); height: expression(this.height > 150 ? 150 : true); } </style> <![endif]-->
casey
+1  A: 

setTimeout() may be a workaround if you are really stuck. Just set it for 2 or 3 seconds - or after the page is expected to load.

EDIT: You may want to have a look at this article - all the way at the bottom about IE mem leaks...

StingyJack
+2  A: 

I've noticed that Firefox and Safari both fire "load" events on new images no matter what, but IE 6&7 only fire "load" if they actually have to get the image from the server -- they don't if the image is already in local cache. I played with two solutions:

1) Give the image a unique http argument every time, that the web server ignores, like

<img src="mypicture.jpg?keepfresh=12345" />

This has the downside that it actually defeats caching, so you're wasting bandwidth. But it might solve the problem without having to screw with your JavaScript.

2) In my app, the images that need load handlers are being inserted dynamically by JavaScript. Instead of just appending the image, then building a handler, I use this code, which is tested good in Safari, FF, and IE6 & 7.

document.body.appendChild(newPicture);
if(newPicture.complete){
    doStuff.apply(newPicture);
}else{
    YAHOO.util.Event.addListener(newPicture, "load", doStuff);
}

I'm using YUI (obviously) but you can attache the handler using whatever works in your framework. The function doStuff expects to run with this attached to the affected IMG element, that's why I call it in the .apply style, your mileage may vary.

Jeremy Wadhams
Thank you very much for this reply! I would have never thought of this myself!
Igor Zinov'yev
+1  A: 

Edit: Due to the structure of our app, it is impractical (bordering on impossible) to run this script in the document's onload.

It is always possible to add handlers to window.onload (or any event really), even if other frameworks, library or code attaches handlers to that event.

<script type="text/javascript">
function addOnloadHandler(func) {
    if (window.onload) {
        var windowOnload = window.onload;
        window.onload = function(evt) {
            windowOnload(evt);
            func(evt);
        }
    } else {
        window.onload = function(evt) {
            func(evt);
        }
    }
}

// attach a handler to window.onload as you normally might
window.onload = function() { alert('Watch'); };

// demonstrate that you can now attach as many other handlers
// to the onload event as you want
addOnloadHandler(function() { alert('as'); });
addOnloadHandler(function() { alert('window.onload'); });
addOnloadHandler(function() { alert('runs'); });
addOnloadHandler(function() { alert('as'); });
addOnloadHandler(function() { alert('many'); });
addOnloadHandler(function() { alert('handlers'); });
addOnloadHandler(function() { alert('as'); });
addOnloadHandler(function() { alert('you'); });
addOnloadHandler(function() { alert('want.'); });
</script>

This answer has a slightly different version of my addOnloadHandler() code using attachEvent. But I discovered in testing that attachEvent doesn't seem to guarantee the handlers fire in the order you added them, which may be important. The function as presented guarantees handlers are fired in the order added.

Note that I pass evt into the added event handlers. This is not strictly necessary and the code should work without it, but I work with a library that expects the event to be passed to the onload handler and that code fails unless I include it in my function.

Grant Wagner
A: 
Max Klymyshyn