Detect when an iframe is loaded

2019-01-12 05:49发布

I'm using an <iframe> (I know, I know, ...) in my app (single-page application with ExtJS 4.2) to do file downloads because they contain lots of data and can take a while to generate the Excel file (we're talking anything from 20 seconds to 20 minutes depending on the parameters).

The current state of things is : when the user clicks the download button, he is "redirected" by Javascript (window.location.href = xxx) to the page doing the export, but since it's done in PHP, and no headers are sent, the browser continuously loads the page, until the file is downloaded. But it's not very user-friendly, because nothing shows him whether it's still loading, done (except the file download), or failed (which causes the page to actually redirect, potentially making him lose the work he was doing).

So I created a small non-modal window docked in the bottom right corner that contains the iframe as well as a small message to reassure the user. What I need is to be able to detect when it's loaded and be able to differenciate 2 cases :

  • No data : OK => Close window
  • Text data : Error message => Display message to user + Close window

But I tried all 4 events (W3Schools doc) and none is ever fired. I could at least understand that if it's not HTML data returned, it may not be able to fire the event, but even if I force an error to return text data, it's not fired.

If anyone know of a solution for this, or an alternative system that may fit here, I'm all ears ! Thanks !

EDIT : Added iframe code. The idea is to get a better way to close it than a setTimeout.

var url = 'http://mywebsite.com/my_export_route';

var ifr = $('<iframe class="dl-frame" src="'+url+'" width="0" height="0" frameborder="0"></iframe>');
ifr.appendTo($('body'));

setTimeout(function() {
    $('.dl-frame').remove();
}, 3000);

13条回答
【Aperson】
2楼-- · 2019-01-12 06:09

If you are doing a file download from a iframe the load event wont fire :) I was doing this a week ago. The only solution to this problem is to call a download proxy script with a tag and then return that tag trough a cookie then the file is loaded. min while yo need to have a setInterval on the page witch will watch for that specific cookie.

// Jst to clearyfy

var token = new Date().getTime(); // ticks
$('<iframe>',{src:"yourproxy?file=somefile.file&token="+token}).appendTo('body');

var timers = [];
timers[timers.length+1] = setInterval(function(){
var _index = timers.length+1;
 var cookie = $.cooke(token);
 if(typeof cookie != "undefined"){
  // File has been downloaded
   $.removeCookie(token);
   clearInterval(_index);
 }
},400);

in your proxy script add the cookie with the name set to the string sent bay the token url parameter.

查看更多
相关推荐>>
3楼-- · 2019-01-12 06:11

In regards to your comment about to get a better way to close it instead of setTimeout. You could use jQuery fadeOut option or any of the transitions and in the 'complete' callback remove the element. Below is an example you can dump right into a fiddle and only need to reference jQuery.

I also wrapped inside listener for 'load' event to not do the fade until the iFrame has been loaded as question originally was asking.

// plugin your URL here 
var url = 'http://jquery.com';

// create the iFrame, set attrs, and append to body 
var ifr = $("<iframe>") 
    .attr({
        "src": url, 
        "width": 300,
        "height": 100,
        "frameborder": 0 
    }) 
    .addClass("dl-frame")
    .appendTo($('body'))
;

// log to show its part of DOM 
console.log($(".dl-frame").length + " items found"); 

// create listener for load 
ifr.one('load', function() {
    console.log('iframe is loaded'); 

    // call $ fadeOut to fade the iframe 
    ifr.fadeOut(3000, function() {
        // remove iframe when fadeout is complete
        ifr.remove();  
        // log after, should no longer exist in DOM
        console.log($(".dl-frame").length + " items found");
    });  
}); 
查看更多
别忘想泡老子
4楼-- · 2019-01-12 06:11

Try this:
Note: You should be on the same domain.

var url = 'http://mywebsite.com/my_export_route',
    iFrameElem = $('body')
        .append('<iframe class="dl-frame" src="' + url + '" width="0" height="0" frameborder="0"></iframe>')
        .find('.dl-frame').get(0),
    iDoc = iFrameElem.contentDocument || iFrameElem.contentWindow.document;

$(iDoc).ready(function (event) {
    console.log('iframe ready!');
    // do stuff here
});
查看更多
Juvenile、少年°
5楼-- · 2019-01-12 06:13

What you are saying could be done for images and other media formats using $(iframe).load(function() {...});

For PDF files or other rich media, you can use the following Library: http://johnculviner.com/jquery-file-download-plugin-for-ajax-like-feature-rich-file-downloads/

Note: You will need JQuery UI

查看更多
倾城 Initia
6楼-- · 2019-01-12 06:17

I've tried with jQuery and it worked just fine as you can see in this post.

I made a working example here.

It's basically this:

<iframe src="http://www.example.com" id="myFrame"></iframe>

And the code:

function test() {
    alert('iframe loaded');
}

$('#myFrame').load(test);

Tested on IE11.

查看更多
你好瞎i
7楼-- · 2019-01-12 06:19

I guess I'll give a more hacky alternative to the more proper ways of doing it that the others have posted. If you have control over the PHP download script, perhaps you can just simply output javascript when the download is complete. Or perhaps redirect to a html page that runs javascript. The javascript run, can then try to call something in the parent frame. What will work depends if your app runs in the same domain or not

 

Same domain

Same domain frame can just use frame javascript objects to reference each other. so it could be something like, in your single page application you can have something like

window.downloadHasFinished=function(str){ //Global pollution. More unique name?
    //code to be run when download has finished
}

And for your download php script, you can have it output this html+javascript when it's done

<script>
if(parent && parent.downloadHasFinished)
    parent.downloadHasFinished("if you want to pass a data. maybe export url?")
</script>

 

Different Domains

For different domains, We can use postMessage. So in your single page application it will be something like

$(window).on("message",function(e){
    var e=e.originalEvent
    if(e.origin=="http://downloadphp.anotherdomain.com"){ //for security
      var message=e.data //data passed if any
      //code to be run when download has finished
    }
});

and in your php download script you can have it output this html+javascript

<script>
parent.postMessage("if you want to pass data",
   "http://downloadphp.anotherdomain.com");
</script>
  1. Parent Demo
  2. Child jsfiddle

 

Conclusion

Honestly, if the other answers work, you should probably use those. I just thought this was an interesting alternative so I posted it up.

查看更多
登录 后发表回答