Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Waiting for multiple iFrames to load before executing function

Forgive my naivety, this probably is quite obvious, I just can't see it now.

Please tell me what is wrong with the following code:

    $('#iframe1').load(function(){
      $('#iframe2').load(function(){
        alert('loaded!');
      });
    }); 

The idea is to wait until both iframes have fully loaded, then alert "loaded" - of course this is a simplified example for the sake of stack.

The script sits in script tags at the end of the body of the html doc.

like image 301
asimovwasright Avatar asked Jan 07 '23 13:01

asimovwasright


2 Answers

@Quertiy answer is perfectly fine, but not very jQuery-ish. It is hard-coded for 2 iframes only.

The beauty of jQuery is that you can make it work for the most number of people, with as little friction as possible.

I've advised a very simplistic plugin that does nearly what is present on that answer, but in a more open way. It not only works on iframes, but also on images, audio, video and whatever has a onload event!

Without further due, here's the code:

(function($){
    $.fn.extend({allLoaded: function(fn){
        if(!(fn instanceof Function))
        {
            throw new TypeError('fn must be a function');
        }

        var $elems = this;
        var waiting = this.length;

        var handler = function(){
            --waiting;
            if(!waiting)
            {
                setTimeout(fn.bind(window), 4);
            }
        };

        return $elems.one('load.allLoaded', handler);
    }});
})(window.jQuery);

It works by adding a load handler to every element in that selection. Since it is a plugin, you can use in whatever way you decide to use it.

Here's an example, that loads 30 random images:

//plugin code
(function($){
	$.fn.extend({allLoaded: function(fn){
		if(!(fn instanceof Function))
		{
			throw new TypeError('fn must be a function');
		}
		
		var $elems = this;
		var waiting = this.length;
		
		var handler = function(){
			--waiting;
			if(!waiting)
			{
				setTimeout(fn.bind(window), 4);
			}
		};
		
		return $elems.one('load.allLoaded', handler);
	}});
})(window.jQuery);



$(function(){

	//generates the code for the 30 images
	for(var i = 0, html = ''; i < 30; i++)
		html += '<img data-src="http://lorempixel.com/g/400/200/?_=' + Math.random() + '">';
	
	//stuffs the code into the body
	$('#imgs').html(html);
	
	//we select all images now
	$('img')
		.allLoaded(function(){
			//runs when done
			alert('loaded all')
		})
		.each(function(){
			//the image URL is on a `data` attribute, to delay the loading
			this.src = this.getAttribute('data-src')
		})

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js"></script>

<div id="imgs"></div>

Your problem, as said before many times, is that you have a load event attached to your iframe. That event is fired everytime the content change.

After that, you set a new event on #iframe2. When it's content changes, it will fire events left and right, above and beyound what you wish!

The best aproach is to keep track of which ones you loaded or not. After all have been loaded, you simply run the function.

like image 95
Ismael Miguel Avatar answered Jan 09 '23 03:01

Ismael Miguel


The problem is that you're waiting until #iframe1 loads before you attach a handler for #iframe2 loading. So if #iframe2 loads first, you'll never get your callback.

Instead, watch the load event on both of them and track which ones you've seen:

var seen1 = false,
    seen2 = false;
$('#iframe1, #iframe2').load(function(){
    if (this.id == "iframe1") {
        seen1 = true;
    } else {
        seen2 = true;
    }
    if (seen1 && seen2) {
        alert('loaded!');
    }
});
like image 24
T.J. Crowder Avatar answered Jan 09 '23 03:01

T.J. Crowder