views:

308

answers:

3

Chrome is wrongly reporting width and height values for images during, or just after, load time. Jquery is used in this code example:

<img id='image01' alt='picture that is 145x134' src='/images/picture.jpg' />

<script>
 var img = $( 'img#image01' )

 img.width() // would return 145 in Firefox and 0 in Chrome.
 img.height() // would return 134 in Firefox and 0 in Chrome.
</script>

If you put the script in a $(function(){}) function, the result is the same. but if you run the code a few seconds after the page has loaded, chrome returns the correct result.

<script>
  function example () {
    var img = $( 'img#image01' );

    img.width() // returns 145 in both Firefox and Chrome.
    img.height() // returns 134 in both Firefox and Chrome.
  }
  window.setTimeout( example, 1000 )
</script>

Also if you specify the width and height values in the img tag, the script seems to work as expected in both Firefox and Chrome.

<img id='image01' src='/images/picture.jpg' width=145 height=134 />

But as you cannot always control the html input, this is not an ideal workaround. Can jQuery be patched with a better workaround for this problem? or will I need to specify the width and height for every image in my code?

+2  A: 

This is not an error. The img element is 0x0 wide until the image is loaded. You should wait until the image is entirely loaded. Try something like this:

$("img#image01").load(function(){
  //do things here
});
Emil Vikström
A: 

Did you try to put that code into

$(document).ready(function(){
});

if that doesn't help, use jQuerys .load() function

var $img = $('#image01'), iwidth = 0, iheight = 0;

$(document).ready(function(){
   $img.load(function(){
        var img = $('#image01');

        img.removeAttr('width').removeAttr('height').css({width: '', height: ''});

        iwidth  = this.width;
        iheight = this.height;
   });
});

$img[0].src = '';
$img[0].src = $img[0].src;
jAndy
this seems to be going a bit overboard? If I had 100< images...
NixNinja
+8  A: 

Your quoted code doing it inline is getting a reasonable result, as the image may well not be loaded before your code executes. You said that you put it in an onload handler and that also has the same result. Did you really mean an onload handler? Because I cannot replicate that result. Note that the jQuery ready function is not an onload handler, it happens much earlier than that. To ensure images are loaded, use the image's load event or the window load event. jQuery ready happens a lot earlier than that.

So this should work:

$(window).bind('load', function() {
    var img = $("#theimage");
    log("Width: " + img.width());
    log("Height: " + img.height());
});

...(where log is something that outputs to the page), and this should work:

$(document).ready(function() {
    $("#theimage").bind('load', function() { // BUT SEE NOTE BELOW
        var img = $(this);
        log("Width: " + img.width());
        log("Height: " + img.height());
    });
});

...but this will not:

$(document).ready(function() {
    var img = $("#theimage");
    log("Width: " + img.width());
    log("Height: " + img.height());
});

...because it happens too soon.

Edit: I really, really should have said in the above: If you're looking the image's load event (rather than the window load event), you need to check to make sure the image hasn't already been loaded, because its load event may have already fired. That would look something like this:

$(document).ready(function() {
    var img, imgElement;

    // Find the image
    img = $("#theimage");
    imgElement = img[0];
    if (imgElement && imgElement.complete) {
        // Already loaded, call the handler directly
        loadHandler.call(imgElement);
    }
    else {
        // Not loaded yet, hook the event
        img.bind('load', loadHandler);
    }

    function loadHandler() {
        var img = $(this);
        log("Width: " + img.width());
        log("Height: " + img.height());
        img.unbind('load', loadHandler);
    }
});

That will call loadHandler at the earliest opportunity, whether the image load won the race or the ready code won the race.

T.J. Crowder
mm... I thought a jquery's $(function(){/* code */}) was an onload... u learn something new every day
NixNinja
but why does chrome and FF differ on this?
NixNinja
@Stefan: If you're doing it before the load event fires, it's a race condition, and the results will tend not to be deterministic. It may well be that Chrome's V8 JavaScript engine is just that much faster. ;-) (It is *freaky* fast.)
T.J. Crowder
and here I was hating chrome for being faster at something
NixNinja
@Stefan: I just added something to the answer I really, really should have included originally: If you use the *image's* `load` event, you need to be sure to check the `complete` flag on the image element first to be sure it has *already* been loaded (e.g., to be sure it didn't win the race).
T.J. Crowder