[英]Detect when an iframe is loaded
我在我的應用程序(使用ExtJS 4.2的單頁應用程序)中使用<iframe>
(我知道,我知道......)來執行文件下載,因為它們包含大量數據並且可能需要一段時間才能生成Excel文件(根據參數,我們在20秒到20分鍾之間說話)。
當前的狀態是:當用戶點擊下載按鈕時,他被Javascript( window.location.href = xxx
)“重定向”到執行導出的頁面,但是因為它是在PHP中完成的,所以沒有發送頭文件,瀏覽器不斷加載頁面,直到下載文件。 但它不是非常用戶友好,因為沒有任何東西顯示它是否仍在加載,完成(文件下載除外)或失敗(這導致頁面實際重定向,可能使他失去他正在做的工作)。
所以我創建了一個停靠在右下角的小型非模態窗口,其中包含iframe以及一條小消息以確保用戶放心。 我需要的是能夠檢測它何時被加載並且能夠區分2種情況:
但是,我嘗試了所有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>
老實說,如果其他答案有效,你應該使用那些。 我只是覺得這是一個有趣的選擇,所以我發布了它。
試試這個(模式)
$(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")
});
您可以使用此庫。 您的代碼片段類似於:
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.