I want to run some code when an image is loaded. Also, I'd like to do it unobtrusively (not inline). More specifically, I want to use jQuery's live() function so it will happen for any dynamically loaded images.
I've tried:
<img class="content_image" alt="" src="..." />
<script>
$('.content_image').live('load', function() {
alert('loaded!');
});
</script>
In addition to load, I've tried onload, and onLoad. When I replace with 'click' all works as expected so I know it's not some interfering bug.
I haven't been able to find a list of available event types for the live() function, so for all I know, it may not be possible.
(It would be load, not onload or onLoad.)
load doesn't bubble (according to the img entry in the HTML5 spec, it's a "simple event", which don't bubble), so you can't use it with live or delegate, which rely on the event bubbling from an element to its ancestor element(s).
You'll have to hook it on the individual img elements (and do so before you set their src, since otherwise you can miss it; and always remember to watch for error as well). (Yes, you really can miss it: The browser is not single-threaded, just the JavaScript main thread. If you set src and the image is in cache or becomes available soon enough, the browser can fire the event. The way events are fired is that the browser looks to see what handlers are registered as of when the event is fired, and queues those to be called when the JavaScript main thread yields back to the browser. If there are no handlers registered, they aren't queued, and you never get the callback.)
it's a little bit dirty, but it works :
<script type="text/javascript">
var loop = setInterval(function() {
// the img to watch
var $img = $("img.hi");
if( !!$img.length && $img[0].complete ) {
// clear the timer
clearInterval(loop);
alert("loaded !");
}
}, 30);
</script>
<img class="hi" src="http://www.google.fr/images/nav_logo72.png" />
Finaly I stumbled upon this jQuery plugin :
https://github.com/desandro/imagesloaded
Related
I have multiple images on my page. To detect broken images , I used this found on SO.
$('.imgRot').one('error',function(){
$(this).attr('src','broken.png');
});
This works fine on the first image which I understand. But when I change this to
$('.imgRot').on('error',function(){
$(this).attr('src','broken.png');
});
it does not work on any of the images . Could someone tell me why ?
Community wiki: This generic answer does not contribute to the question OP posted but relative to the title.
The concept of one() and on() can be explained with the below code.
one() function is automatically moved to off state after first instance of occurance.
on() is identical to one() but it needs to be manually put to off state otherwise the number of instances has no limit.
var i = 1;
$('.one').one('click', function() {
$(this).text('I am clickable only once: ' + i);
i++;
});
var j = 1;
$('.multiple').on('click', function() {
$(this).text('I was clicked ' + j + ' times');
j++;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="one">Click me</div>
<div class="multiple">Click me</div>
If you look at the source code for .one() in jQuery 1.7, it just calls .on() internally except that after the event fires, it removes the event handler. So, there should be no difference in your case because error is an event that should only happen once per object anyway.
So, there must be something else going on in your code like maybe the image objects haven't been loaded into the DOM yet when you run this code or something like that.
If you were trying to use delegated event handling to do this (which your example does not show), then you may run into issues where the 'error' event doesn't propagate.
It may also be that your code has timing issues due to caching. Trying to install these types of error handlers on images that are already in the DOM is a race condition. You're trying to get the error handler installed before it gets called, but the image has already started loading and the event might have already fired before you get the event handler installed. Subsequent page loads (after the first) may have cached other page elements or DNS references so it may get to the error handler quicker and perhaps even before your JS can run and install the error handlers.
I know this is an issue with browser caching and the onload event. You can only reliably get the onload event if you attach the event handler either in the embedded HTML (so it's there when the <img> tag is first parsed or if you attach it before the .src property has been set (if creating the image programmatically). That would suggest that you can't reliably set error handlers the way you are doing for images that are in the page HTML.
My suggestion would be this:
Don't try to install error handlers like this after the images are in the DOM.
If you assign them on programmatically generating images, then assign the event handlers before .src is assigned.
If you need these on images in the page's HTML, then you will have to put the event handlers in the HTML with something like <img src="xxx" onerror="yourErrorFunc(this)"> because that's the only way to guarantee that the handlers are installed before the event can occur.
jQuery will reuse the "on" method for the "one". Following is the internal code of jQuery where they'll be passing hardcoded value "1" to the function of jQuery.on() They'll turn off the triggered event further on the element using jQuery.off()
on:function( types, selector, data, fn, one ) {
if ( one === 1 ) {
origFn = fn;
fn = function( event ) {
jQuery().off( event );
return origFn.apply( this, arguments );
};
}
off:function(types, selector, data, fn){
on(types, selector, data, fn, 1);
}
So, in your case "error" is the event type triggered on the first image and when the jQuery.one() method called this event got turned off and then its not triggered for further on the $('.imgRot') elements
I was surprised to find the following doesn't appear to work, insofar as the DOMContentLoaded event doesn't fire (this.els is an object of elements).
this.els.stage_ifr.prop('src', 'templates/'+ee.globals.creating+'/item'+this.id);
this.els.stage_ifr[0].addEventListener('DOMContentLoaded', function() {
alert('loaded!');
}, false);
The page loads into the iframe fine, but no callback. The DOM level zero onload, however, works.
this.els.stage_ifr[0].onload = function() { alert('loaded!'); }; //<-- fires
A workaround is to prepare a globally-accessible jQuery deferred object in the parent page and resolve it via a DOM-ready event fired from the page called into the iframe, rather than listening for DOM-ready from the parent.
Paraent page:
dfd = new $.Deferred;
dfd.done(function() { alert("frame page's DOM is ready!"); });
Frame page:
$(function() { window.parent.dfd.resolve(); });
Nonetheless it would be good to know what's up with the first approach...
In the process of answering this question, I discovered the reason your DOMContentLoaded event listener doesn't work. It appears to me that you have two issues.
First, you're trying to listen for the DOMContentLoaded event on the iFrame itself. That isn't an iFrame event. It is a document event. So you have to reach into the iFrame to get the contentWindow and then get the document from that. That leads into the second issue.
Second, when an iFrame is first created, it has a dummy document in it that is NOT the same document as will eventually be there when dynamic content is loaded via the .src attribute. So, even if you did:
this.els.stage_ifr.contentWindow.document
to get the document in the iFrame, it will not necessarily be the right document and thus the DOMContentLoaded event won't fire on it (I've seen this behavior in Chrome).
MDN says that one can listen for the DOMFrameContentLoaded event on the iFrame itself and this will correspond with when the underlying document actually gets DOMContentLoaded. Unfortunately, I can't get this event to work in any browser. So, at this moment, the only work-around I know of is to either trigger the load event from within the iFrame itself where it can listen to its own DOMContentLoaded event (it can call out to the parent window if need be) or to just listen for the load event on the iFrame object and know that it won't fire until resources such as style sheets and images in the iFrame are also loaded.
Anyway, I thought I'd explain some of what was going on with your initial code and offer another solution even though this question was posted more than a year ago (though never answered).
Update:
I've developed a method of tracking DOMContentLoaded for an iFrame loaded with the same origin as its parent. You can see the code here.
After trying different options I found that the following code works for me:
var iframe = document.getElementById("app-frame-id");
iframe.contentWindow.addEventListener("DOMContentLoaded", onFrameDOMContentLoaded, true);
function onFrameDOMContentLoaded () {
console.log("DOMContentLoaded");
};
If your page and the iframe are on the same domain you have to wait for the original page to fire DOMContentLoaded first, then attach a DOMContentLoaded event listener on the iframe's Window (not Document).
Given you have an iframe as follows,
<iframe id="iframe-id" name="iframe-name" src="..."></iframe>
the next snippet will allow you to hook into the iframe's DOMContentLoaded event:
document.addEventListener('DOMContentLoaded', function () {
var iframeWindow = frames['iframe-name'];
// var iframeWindow = document.querySelector('#iframe-id').contentWindow
// var iframeWindow = document.getElementById('iframe-id').contentWindow
iframeWindow.addEventListener('DOMContentLoaded', function () {
console.log('iframe DOM is loaded!');
});
});
This might be more of a hack, but helped me solve a similar issue.
I am listening for onmouseenter of the frame content now.
This event triggers ahead of load (if the user moves the mouse). But as in my case I need the event for initialising a context menu, using the mouse was a precondition anyway.
It even triggers when the frame content changes under the mouse.
I am wondering why I can´t attach an eventlistener to the onload event of an html image.
<img id="imageID" onload="doSomething();"> works perfectly,
but if I want to add a listener like
document.getElementById('imageID').addEventListener('onload', function(e) {
doSomething();
});
or with jQuery
$("imageID").on("onload", function(){
doSomething();
});
the event is not being catched. If I listen for a simple "click"-Event it works, but not with the "onload"-Event.
Can anybody tell my the reason for this behaviour?
Don't include "on" in the event name.
// no "on"------------v
document.getElementById('imageID').addEventListener('load', function(e) {
// no "on"----v
$("#imageID").on("load", function(){
// ^----need "#"
There are two problems here.
You don't put "on" in the event name, as I Hate Lazy said.
If you're trying to hook the load event on an image that's already on the page, you'll have a race condition and sometimes won't see the event, because it can be "loaded" the instant that it is put on the page (if the relevant image is in cache). You have to hook load before the image has a src attribute, which typically means adding the image dynamically. Your DOM0 handler (the onload="doSomething()") works because it's already attached when the image is added to the document, but anything looking the image up on the page later (getElementById or jQuery) may miss it.
I am using <img> onload function to trigger the resize action on the image that has been loaded. But I found if in one webpage there is two or more <img>s with the same "src", which means the image is the same, the latter <img>'s onload() function will not be invoked. How to make sure that all the <img>s are properly resized even when there are some <img>s with the same src?
Thanks.
This is happening because your browser is actually caching the first image. So when you attempt to load the same "src" again, it's cached locally and never actually fires an onload. To get it to load at all times, you could append on a unique value to the query string.
Instead of using a separate onload handler for each <img/>, why not just use a single onload event for the window? This event fires after everything (like all the images) have loaded, so you could just process each image in that single handler. Something like:
function processAllImages()
{
var imgElts = document.getElementsByTagName('img');
for (var i=0; i<imgElts.length; i++)
{
processImage(imgElts[i]); // TODO write this function
}
}
window.onload = processAllImages;
I asked similar question before. Check this out: Javascript/jQuery: How to detect img is fully downloaded or not?
In short, you can use 'complete' property of img tag.
check it before you bind load event.
how to handle onLoad Event on img tag?
example:
$ ('img'). onload (function (e) {
alert ('image loaded' + $ (this), attr ('src'));
})
is like the imageLoader
and others tags like script, link, etc...
jQuery callback on image load (even when the image is cached)
$("img").one('load', function() {
// do stuff
}).each(function() {
if(this.complete) $(this).load();
});
Seems to work for me, and should fix the caveat with caching.
There's an example of how to do this right on the jQuery doc page for .load().
HTML:
<img src="book.png" alt="Book" id="book" />
Javascript:
$('#book').load(function() {
// Handler for .load() called.
});
And, there are a bunch of caveats to watch out for listed on that jQuery page too.
With images, if you want to assure that the load handler gets called, the load handler has to be set before the image can possibly be loaded. If you're constructing the image in code, that means you have to set the load handler before you set the .src attribute. That's because, in some browsers, if the image is in the memory/disk cache, it will load immediately when you assign .src and if you then subsequently set the .load() handler, you will be too late (the image will already be loaded) and the .load() handler will never be called.
I don't know how to guarantee that a jQuery-based .load() always gets called for an image that's in your page HTML because you can't assign the event handler until the object exists, but once the object it exits, it might already be loaded. You can use non-jQuery setting of an onload=xxx in the actual HTML though that is an older style of coding. Of course, you can always hook window.onload to know when all page images (and other resources) have been loaded.
jQuery has a load method, not onload. And your code should probably be:
$ ('img').load(function () {
alert('image loaded ' + this.src);
})