检测何时加载iframe

时间:2014-05-20 17:13:57

标签: javascript iframe

我在我的应用程序(使用ExtJS 4.2的单页应用程序)中使用<iframe>(我知道,我知道......)来执行文件下载,因为它们包含大量数据并且可以使用而生成Excel文件(根据参数我们说的是20秒​​到20分钟)。

当前的状态是:当用户点击下载按钮时,他被Javascript(window.location.href = xxx)“重定向”到执行导出的页面,但是因为它是在PHP中完成的,所以没有标题是发送后,浏览器不断加载页面,直到文件被下载。但它不是非常用户友好,因为没有任何东西显示它是否仍在加载,完成(文件下载除外)或失败(这导致页面实际重定向,可能使他失去他正在做的工作)。

所以我创建了一个停靠在右下角的小型非模态窗口,其中包含iframe以及一条小消息以确保用户放心。我需要的是能够检测它何时被加载并且能够区分两种情况:

  • 无数据:OK =&gt;关闭窗口
  • 文字数据:错误讯息=&gt;向用户显示消息+关闭窗口

但是我尝试了所有4个事件(W3Schools doc)并且没有曾经被解雇。我至少可以理解,如果它不是返回的HTML数据,它可能无法触发事件,但即使我强制错误返回文本数据,它也不会被触发。

如果有人知道这方面的解决方案,或者可能适合这里的替代系统,我全都耳朵!谢谢!

编辑:添加了iframe代码。我们的想法是获得一种比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 个答案:

答案 0 :(得分:7)

我想知道它是否需要在前端和后端代码中进行一些重大更改,但您是否考虑过使用AJAX?工作流程将是这样的:用户发送AJAX请求以启动文件生成,并且前端不断地从服务器轮询它的状态,当它完成时 - 向用户显示下载链接。我相信工作流程会更直接。

嗯,你也可以试试这个伎俩。在父窗口中为iframe的完整加载myOnLoadCallback创建一个回调函数,然后使用parent.myOnLoadCallback()从iframe中调用它。但是你仍然需要使用setTimeout来处理服务器错误/连接超时。

最后一件事 - 你是怎么试图抓住iframe的事件的?也许它与浏览器有关。您是否尝试过直接在HTML属性中设置事件回调?喜欢

<iframe onload="done()" onerror="fail()"></iframe>

这是一个不好的做法,我知道,但有时需要完成工作快速,是吗?

<强>更新 好吧,我担心你需要用JS调试器度过漫长而痛苦的一天。 load事件工作。不过我还是有一些建议:

1)尝试在设置元素src之前设置事件监听器。也许onload事件如此之快,以至于它在创建元素和设置事件的回调之间滑动

2)同时尝试检查您的服务器代码是否与iframe很好地匹配。我做了一个简单的测试,试图从Dropbox下载PDF,尝试用你支持的路由替换我的URL。

<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<iframe id="book"></iframe>
<button id="go">Request downloads!</button>

<script>
    var bookUrl = 'https://www.dropbox.com/s/j4o7tw09lwncqa6/thinkpython.pdf';

    $('#book').on('load', function(){
        console.log('WOOT!', arguments);
    });

    $('#go').on('click', function(){
        $('#book').attr('src', bookUrl);
    });
</script>

更新2

3)另外,请查看浏览器调试器的“网络”选项卡,将src设置为iframe时会发生什么情况,它应显示请求和服务器对标头的响应。

答案 1 :(得分:3)

我已经尝试过使用jQuery,但是你可以在这篇文章中看到它正常工作。

我做了一个工作示例here

基本上就是这样:

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

代码:

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

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

在IE11上测试。

答案 2 :(得分:2)

也许你应该使用

$($('.dl-frame')[0].contentWindow.document).ready(function () {...})

答案 3 :(得分:2)

您可以使用以下脚本。它来自我的一个项目。

$("#reportContent").html("<iframe id='reportFrame' sandbox='allow-same-origin allow-scripts' width='100%' height='300' scrolling='yes' onload='onReportFrameLoad();'\></iframe>");

答案 4 :(得分:2)

我想我会给其他人发布的更合适的方法提供更多的hacky替代方法。如果您可以控制PHP下载脚本,也许只需在下载完成后输出javascript即可。或者可能重定向到运行javascript的html页面。 javascript运行,然后可以尝试在父框架中调用某些东西。什么工作取决于您的应用程序是否在同一个域中运行

相同域名

相同的域框架只能使用框架javascript对象相互引用。所以它可能是这样的,在你的单页面应用程序中你可以有像

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

对于你下载的php脚本,你可以在完成后输出这个html + javascript

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

不同的域名

对于不同的域名,我们可以使用postMessage。因此,在您的单页面应用程序中,它将类似于

$(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
    }
});

并在您的php下载脚本中,您可以输出此html + javascript

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

    老实说,如果其他答案有效,你应该使用那些。我只是觉得这是一个有趣的选择,所以我发布了它。

答案 5 :(得分:2)

试试这个(模式)

    $(function () {
        var session = function (url, filename) {
           // `url` : URL of resource
           // `filename` : `filename` for resource (optional)
            var iframe = $("<iframe>", {
                "class": "dl-frame",
                    "width": "150px",
                    "height": "150px",
                    "target": "_top"
            })
            // `iframe` `load` `event`
            .one("load", function (e) {
                $(e.target)
                    .contents()
                    .find("html")
                    .html("<html><body><div>" 
                          + $(e.target)[0].nodeName 
                          + " loaded" + "</div><br /></body></html>");
                alert($(e.target)[0].nodeName 
                        + " loaded" + "\nClick link to download file");
                return false
            });

            var _session = $.when($(iframe).appendTo("body"));
            _session.then(function (data) {
                var link = $("<a>", {
                        "id": "file",
                        "target": "_top",
                        "tabindex": "1",
                        "href": url,
                        "download": url,
                        "html": "Click to start {filename} download"
                });
                $(data)
                    .contents()
                    .find("body")
                    .append($(link))
                    .addBack()
                    .find("#file")
                    .attr("download", function (_, o) {
                      return (filename || o)
                    })
                    .html(function (_, o) {
                      return o.replace(/{filename}/, 
                      (filename || $(this).attr("download")))
                })

            });
            _session.always(function (data) {
                $(data)
                    .contents()
                    .find("a#file")
                    .focus()
                    // start 6 second `download` `session`,
                    // on `link` `click`
                    .one("click", function (e) {
                    var timer = 6;
                    var t = setInterval(function () {
                        $(data)
                            .contents()
                            .find("div")
                             // `session` notifications
                            .html("Download session started at " 
                                  + new Date() + "\n" + --timer);
                    }, 1000);
                    setTimeout(function () {
                        clearInterval(t);
                        $(data).replaceWith("<span class=session-notification>"    
                          + "Download session complete at\n" 
                          + new Date() 
                          + "</span><br class=session-notification />"
                          + "<a class=session-restart href=#>"
                          + "Restart download session</a>");
                        if ($("body *").is(".session-restart")) {
                            // start new `session`,
                            // on `.session-restart` `click`
                            $(".session-restart")
                            .on("click", function () {
                                $(".session-restart, .session-notification")
                                .remove() 
                                // restart `session` (optional),
                                // or, other `session` `complete` `callback` 
                                && session(url, filename ? filename : null)
                            })
                        };
                    }, 6000);
                });
            });
        };
        // usage
        session("http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf", "ECMA_JS.pdf")
    });

jsfiddle http://jsfiddle.net/guest271314/frc82/

答案 6 :(得分:1)

您可以使用this库。您的代码片段类似于:

window.onload = function () {
  rajax_obj = new Rajax('',
    {
        action : 'http://mywebsite.com/my_export_route', 
        onComplete : function(response) {
                //This will only called if you have returned any response 
               // instead of file from your export script
               // In your case 2
               // Text data : Error message => Display message to user
        }
    });
}

然后,您可以在下载链接上点击rajax_obj.post()

<a href="javascript:rajax_obj.post()">Download</a>

注意:您应该在PHP脚本中添加一些标题,以便强制下载文件

header('Content-Disposition: attachment; filename="'.$file.'"');
header('Content-Transfer-Encoding: binary');

答案 7 :(得分:1)

关于你的评论是否有更好的方法来关闭它而不是setTimeout。您可以使用jQuery fadeOut选项或任何转换,并在“完整”回调中删除元素。下面是一个示例,您可以直接转储到一个小提琴,只需要引用jQuery。

我还在'load'事件的监听器中包含内容,直到iFrame被加载为最初询问的问题才进行淡入淡出。

// 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");
    });  
}); 

答案 8 :(得分:1)

试试这个:
注意 :您应该在同一个域中。

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
});

答案 9 :(得分:1)

如果你从iframe进行文件下载,那么load事件就不会发生了:)我一周前这样做了。解决此问题的唯一方法是使用标记调用下载代理脚本,然后通过cookie返回该标记,然后加载该文件。分钟,你需要在页面上有一个setInterval,它会监视那个特定的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);

在您的代理脚本中添加cookie,其名称设置为发送的字符串,以及令牌url参数。

答案 10 :(得分:1)

如果您控制生成excel的服务器中的脚本或您发送给iframe的任何内容,为什么不放置UID标志并将其存储在值为0的会话中,所以...当创建iframe时服务器和服务器调用脚本只需将UID标志设置为1,当脚本完成时(iframe将被加载)只需将其置于2。

然后你只需要一个计时器和一个定期的AJAX调用服务器来检查UID标志......如果它被设置为0,那么这个过程没有启动,如果它是1文件正在创建,最后如果是2,则该过程已经结束。

你怎么看?如果您需要有关此方法的更多信息,请询问。

答案 11 :(得分:1)

您可以使用$(iframe).load(function() {...});

对图像和其他媒体格式进行说明

对于PDF文件或其他富媒体,您可以使用以下库: http://johnculviner.com/jquery-file-download-plugin-for-ajax-like-feature-rich-file-downloads/

注意:您将需要JQuery UI

答案 12 :(得分:1)

我能想到两种解决方案。要么你有PHP将它发布到一个MySQL表,其中前端将从使用AJAX调用中获取信息来检查生成的进度。使用访问页面时生成的某些唯一键是多个人同时生成excel文件的理想选择。

另一个解决方案是使用nodejs&amp;然后在PHP中使用cURL或socket到nodejs服务发布excel文件的进度。然后,当在nodejs中从PHP接收更新时,您只需为正确的套接字编写excel文件的进度。这将削减一些浏览器支持。除非你使用外部库来为它提供几乎所有浏览器的websocket支持。版本

希望这个答案有所帮助。我去年有同样的问题。结束了AJAX轮询,即时发布PHP后期进度。