簡體   English   中英

檢測何時加載iframe

[英]Detect when an iframe is loaded

我在我的應用程序(使用ExtJS 4.2的單頁應用程序)中使用<iframe> (我知道,我知道......)來執行文件下載,因為它們包含大量數據並且可能需要一段時間才能生成Excel文件(根據參數,我們在20秒到20分鍾之間說話)。

當前的狀態是:當用戶點擊下載按鈕時,他被Javascript( window.location.href = xxx )“重定向”到執行導出的頁面,但是因為它是在PHP中完成的,所以沒有發送頭文件,瀏覽器不斷加載頁面,直到下載文件。 但它不是非常用戶友好,因為沒有任何東西顯示它是否仍在加載,完成(文件下載除外)或失敗(這導致頁面實際重定向,可能使他失去他正在做的工作)。

所以我創建了一個停靠在右下角的小型非模態窗口,其中包含iframe以及一條小消息以確保用戶放心。 我需要的是能夠檢測它何時被加載並且能夠區分2種情況:

  • 無數據:OK =>關閉窗口
  • 文本數據:錯誤消息=>向用戶顯示消息+關閉窗口

但是,我嘗試了所有4個事件( W3Schools的文檔 ),並沒有被解雇 我至少可以理解,如果它不是返回的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);

我想知道它是否需要在前端和后端代碼中進行一些重大更改,但您是否考慮過使用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時會發生什么,它應顯示請求和服務器對標頭的響應。

我已經嘗試過使用jQuery,它可以正常工作,你可以在這篇文章中看到。

我在這里做了一個工作的例子。

它基本上是這樣的:

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

和代碼:

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

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

在IE11上測試過。

也許你應該使用

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

您可以使用以下腳本。 它來自我的一個項目。

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

我想我會給其他人發布的更合適的方法提供更多的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. 家長演示
  2. 兒童jsfiddle

結論

老實說,如果其他答案有效,你應該使用那些。 我只是覺得這是一個有趣的選擇,所以我發布了它。

試試這個(模式)

    $(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/

您可以使用庫。 您的代碼片段類似於:

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

關於你的評論,以獲得一個更好的方法來關閉它而不是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");
    });  
}); 

試試這個:
注意 :您應該在同一個域中。

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

如果你正在從iframe進行文件下載,那么load事件就不會發生了:)我一周前這樣做了。 解決此問題的唯一方法是使用標記調用下載代理腳本,然后通過cookie返回該標記,然后加載該文件。 min你需要在頁面上有一個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參數。

如果您控制生成excel的服務器中的腳本或您發送到iframe的任何內容,為什么不放置UID標志並將其存儲在值為0的會話中,所以......當創建iframe並調用服務器腳本時UID標志為1,當腳本完成(iframe將被加載)時,只需將其置於2。

然后你只需要一個定時器和一個定期的AJAX調用服務器來檢查UID標志......如果它設置為0,則進程沒有開始,如果它是1,則文件正在創建,最后如果它是2則進程已經結束了。

你怎么看? 如果您需要有關此方法的更多信息,請詢問。

您可以使用$(iframe).load(function() {...});對圖像和其他媒體格式進行說明$(iframe).load(function() {...});

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

注意:您將需要JQuery UI

我能想到兩種解決方案。 要么你有PHP將它發布到一個MySQL表,其中前端將從使用AJAX調用中獲取信息來檢查生成的進度。 使用訪問頁面時生成的某些唯一鍵是多個人同時生成excel文件的理想選擇。

另一個解決方案是使用nodejs然后在PHP中使用cURL或socket到nodejs服務發布excel文件的進度。 然后,當在nodejs中從PHP接收更新時,您只需為正確的套接字編寫excel文件的進度。 這將削減一些瀏覽器支持。 除非你使用外部庫來為它提供幾乎所有瀏覽器和版本的websocket支持。

希望這個答案有所幫助 我去年有同樣的問題。 結束了AJAX輪詢,即時發布PHP后期進度。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM