jQuery: new image added to DOM always has width 0 after loaded
Asked Answered
C

4

9

I need to obtain the dimensions of an image, using a dynamically created image tag. It works. But ONLY using attr('width') and ONLY if I do NOT add the image to the DOM. Otherwise, dimensions returned are zero. Why? :)

this.img = $('<img/>', {'src': e.url} );           // generate image
this.img.appendTo('body');                         // add to DOM
this.img.load(function(self){return function(){    // when loaded call function
    console.log(self.img.attr('src'));
    console.log(self.img.attr('width'));
    console.log(self.img.width());
    console.log(self.img.css('width'));
}; }(this) );                                      // pass object via closure

So I generate the image, add it to the DOM and define a handler function to be called when the image is loaded. I'm using a closure to pass it a reference to the original object, called "self" within the closure. I'm dumping the image's URL to make sure the reference is alright. Output is:

pic.jpg
0
0
0px

Now here is the strange thing: if I remove the second line above (the appendTo), then it suddenly works, but only for attr('width'):

pic.jpg
1024
0
0px

This behavior doesn't make any sense to me. I would expect to get the actual image size in all six above cases. I know now how to work-around the problem, but I'd like to understand why it happens. Is it a bug? Or is there some logical reason for it?

The above results are for Safari. I just tested with Chrome and I got all correct values in the first example, but still two zeros in the second example. Very weird.

Chariness answered 7/12, 2010 at 23:42 Comment(6)
are you sure you always have e.url valid one??Destruction
@travel boy can you give some other name to this variable instead of img this.img , this.newImgSource or so...///Destruction
@Chariness & gov: Agreed, the property holding the jQuery object for image should be changed as img is a tag name, just good practice.Quebec
@gov: I renamed img into ximg and it doesn't change anything. Yes, e.url is valid and has been the same in all tests.Chariness
@Chariness can we do the example i suggested below//just to eliminate one by one.Destruction
appending it to a div (instead of the body) doesn't change anything about the problem.Chariness
C
3

I found a reliable solution for Safari:

  • Create the image
  • Attach the load() handler
  • Only AFTER that, set the src attribute!!

I still don't understand why width should ever return zero, but it seems that unexpected things happen if you attach the load handler only after setting the src. I got that idea only because I encountered yet another problem with IE (which didn't even call the load handler since it considered the image loaded before the handler was attached), so the lesson I've learnt is to always do things in the order described above.

Chariness answered 11/12, 2010 at 3:37 Comment(0)
H
2

tried Your code in firebug and chrome and it behaves as expected :

var src = 'http://static.jquery.com/files/rocker/images/logo_jquery_215x53.gif';

this.img = $('<img/>', { 'src': src } );
this.img.appendTo('body');                         // add to DOM
this.img.load(function(self){return function(){    // when loaded call function
    console.log(self.img.attr('src'));
    console.log(self.img.attr('width'));
    console.log(self.img.width());
    console.log(self.img.css('width'));
 }; }(this) ); 

got the following :

http://static.jquery.com/files/rocker/images/logo_jquery_215x53.gif
215
215
215px

after removing the appendTo() line I'm abble to reproduce the behaviour in Chrome, but I do not find it weird. width() is calculating the actual width for the "displayed" element - e.g. everytime You put the image in a hidden div it will be == 0 !

try replacing the appendTo line with the following and You'll get the same :

this.img.appendTo( $('<div/>').css('display', 'none') );

UPDATE:

Results from Safari 3.1.2 are acting exactly the same for me as Chrome when appended to DOM.

When img is not inserted into the DOM I'm also getting acceptable (no weirdness) results :

http://static.jquery.com/files/rocker/images/logo_jquery_215x53.gif
215
0
0px

I'm convinced there must be something unique to Your setup ... or it is a Safari @ Mac OSX bug (tried Safari on Windows env).

Harwilll answered 10/12, 2010 at 17:11 Comment(2)
The weird thing is something else: in Safari the image has a width of zero when the image IS appended to the DOM. I can only read it's width when I do NOT append it to the DOM. You should find that weird! Admittedly, Chrome is understandable. Safari isn't.Chariness
@Chariness I've updates my answer - I was really wondering to see the weird behaviour in Safari but for me it just worked pretty much correctly ... as @gov bellow mentioned on Your Safari setup the img might be hidden for some reason.Harwilll
Q
0

It seems to me that the way you are assigning the jQuery object and then calling the load function on the image is a bit of an over kill, which makes it hard to see why jQuery might behave thus.

Try this:

this.img = $('<img />').attr('src', e.url).appendTo('body');
console.log('width: ' + this.img.width());

Works perfectly for me to just get it after the image is injected into the DOM.

You could also try this:

this.img = $('<img />').attr('src', e.url).css({
   'width': 'auto',
   'height': 'auto'
}).appendTo('body');
console.log('width: ' + this.img.width());
console.log('height: ' + this.img.height());
Quebec answered 8/12, 2010 at 0:14 Comment(7)
@Dale: your approach is certain to fail unless the image is already in the cache (because the dimensions will be read before the image is loaded). So it cannot be done this way - you need the load function.Chariness
i agree with travelboy , load ensures that image is loaded to the browserDestruction
@Chariness , your code looks perfect , the only otherreason why elements gives 0 is if they are hidden for some reason , is your image parent container hidden or so ??Destruction
@Chariness , are you there?? if so can we do some sample example , create a separate div place holder and attach your image to the div and perform the same code/// i think there is something wrong when it got attached to body , we can start debugging this way ,Destruction
@gov: the img got added at the top level within the body (just before the </body> tag), which may be unusual but it was exactly what I wanted. I just tried your suggestion to instead append it to a div. That doesn't change anything about the problem. It's also noteworthy that Safari behaves different from Chrome. I wonder why jQuery's width() method returns something different from attr('width') in the first place...Chariness
I agree that the load function is more robust (and you should go with that), however, with cache all cleared, trying this over 20 times, it did not fail me once. Works every time.Quebec
@Chariness width() returns the computed style's width, whereas attr('width') returns the width attribute. In fact, there's even a third value, style.width, which returns the width specified by the element style.Flavine
M
0

I don't think your closure is working the way you want. Have you tried something like:

this.img.load(function(){                          // when loaded call function
    console.log(this.attr('src'));
    console.log(this.attr('width'));
    console.log(this.width());
    console.log(this.css('width'));
}); 
Mixer answered 8/12, 2010 at 1:16 Comment(1)
I cannot do it like this because I need a reference to the object containing img. But the closure works (because I can access img and the other properties of the object)Chariness

© 2022 - 2024 — McMap. All rights reserved.