I have a program which will dynamically set an iframe src to load pages. I need to hook a event handler for the page completely loaded. How can i do it? Thanks!
<script type="text/javascript">
function iframeDidLoad() {
alert('Done');
}
function newSite() {
var sites = ['http://getprismatic.com',
'http://gizmodo.com/',
'http://lifehacker.com/']
document.getElementById('myIframe').src = sites[Math.floor(Math.random() * sites.length)];
}
</script>
<input type="button" value="Change site" onClick="newSite()" />
<iframe id="myIframe" src="http://getprismatic.com/" onLoad="iframeDidLoad();"></iframe>
Example at http://jsfiddle.net/MALuP/
Try this:
top.document.getElementById('AppFrame').setAttribute("src",fullPath);
Try this...
function urlChange(url) {
var site = url+'?toolbar=0&navpanes=0&scrollbar=0';
document.getElementById('iFrameName').src = site;
}
TEST
You should also consider that in some Opera versions onload is fired several times and add some hooks:
// fixing Opera 9.26, 10.00
if (doc.readyState && doc.readyState != 'complete') {
// Opera fires load event multiple times
// Even when the DOM is not ready yet
// this fix should not affect other browsers
return;
}
// fixing Opera 9.64
if (doc.body && doc.body.innerHTML == "false") {
// In Opera 9.64 event was fired second time
// when body.innerHTML changed from false
// to server response approx. after 1 sec
return;
}
Code borrowed from Ajax Upload
try this code. then 'formId' div can set the image.
$('#formId').append('<iframe style="width: 100%;height: 500px" src="/document_path/name.jpg"' +
'title="description"> </iframe> ');
Try this:
document.frames["myiframe"].onload = function(){
alert("Hello World");
}
Related
There is a angularjs application, I am working out of it. in my web page, I am getting application loaded in the iframe - I just want to know whether the ifame loaded or not. for that, I use:
//but i don't like this.
var myTimeout = function () {
setTimeout(function(){
if($('.ssueContentIframe').length > 0 ) {
penetrate();
return;
}
myTimeout();
}, 10000);
};
myTimeout();
in the iframe there is number of content keep loading and changing. But one of the event I would require to add in the element with class name of .ui-grid-icon-ok - how can I hear from document that the element existence - any one help me?
If there's no constraints CORS or XSS, you should be able to do something like the following.
var what = document.getElementById('what');
function checkIframeLoaded() {
try {
// Get a handle to the iframe element
var iframe = document.getElementById('iframe');
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
what.appendChild(document.createTextNode("Checking...\n"));
// Check if loading is complete
if (iframeDoc.readyState == 'complete') {
afterLoading();
return;
}
} catch (err) {
alert(err);
}
// If we are here, it is not loaded. Set things up so we check the status again in 100 milliseconds
window.setTimeout(checkIframeLoaded, 100);
}
function afterLoading() {
what.appendChild(document.createTextNode("It loaded!\n"));
}
checkIframeLoaded();
iframe {
width: 100px;
height: 25px;
}
<iframe id="iframe" src="data:text/plain,"></iframe>
<pre id="what">Not loaded.</pre>
If there are any constraints (e.g. sandboxing, CORS, XSS, etc.), you'll get an alert dialog every 10th of a second with the error, and I hope you have the ability to click the checkbox to ignore them. :)
Note: This is a modified version of this other stack overflow answer, expanded to demonstrate errors.
It seems that $('#someIframe').load(function(){...}) won't fire if it is attached after the iframe has finished loading. Is that correct?
What I'd really like is to have a function that is always called once when or after an iframe has loaded. To make this clearer, here are two cases:
Iframe hasn't loaded yet: run a callback function once it loads.
Iframe has already loaded: run the callback immediately.
How can I do this?
I've banged my head against a wall until I found out what's happening here.
Background information
Using .load() isn't possible if the iframe has already been loaded (event will never fire)
Using .ready() on an iframe element isn't supported (reference) and will call the callback immediately even if the iframe isn't loaded yet
Using postMessage or a calling a container function on load inside the iframe is only possible when having control over it
Using $(window).load() on the container would also wait for other assets to load, like images and other iframes. This is not a solution if you want to wait only for a specific iframe
Checking readyState in Chrome for an alredy fired onload event is meaningless, as Chrome initializes every iframe with an "about:blank" empty page. The readyState of this page may be complete, but it's not the readyState of the page you expect (src attribute).
Solution
The following is necessary:
If the iframe is not loaded yet we can observe the .load() event
If the iframe has been loaded already we need to check the readyState
If the readyState is complete, we can normally assume that the iframe has already been loaded. However, because of the above-named behavior of Chrome we furthermore need to check if it's the readyState of an empty page
If so, we need to observe the readyState in an interval to check if the actual document (related to the src attribute) is complete
I've solved this with the following function. It has been (transpiled to ES5) successfully tested in
Chrome 49
Safari 5
Firefox 45
IE 8, 9, 10, 11
Edge 24
iOS 8.0 ("Safari Mobile")
Android 4.0 ("Browser")
Function taken from jquery.mark
/**
* Will wait for an iframe to be ready
* for DOM manipulation. Just listening for
* the load event will only work if the iframe
* is not already loaded. If so, it is necessary
* to observe the readyState. The issue here is
* that Chrome will initialize iframes with
* "about:blank" and set its readyState to complete.
* So it is furthermore necessary to check if it's
* the readyState of the target document property.
* Errors that may occur when trying to access the iframe
* (Same-Origin-Policy) will be catched and the error
* function will be called.
* #param {jquery} $i - The jQuery iframe element
* #param {function} successFn - The callback on success. Will
* receive the jQuery contents of the iframe as a parameter
* #param {function} errorFn - The callback on error
*/
var onIframeReady = function($i, successFn, errorFn) {
try {
const iCon = $i.first()[0].contentWindow,
bl = "about:blank",
compl = "complete";
const callCallback = () => {
try {
const $con = $i.contents();
if($con.length === 0) { // https://git.io/vV8yU
throw new Error("iframe inaccessible");
}
successFn($con);
} catch(e) { // accessing contents failed
errorFn();
}
};
const observeOnload = () => {
$i.on("load.jqueryMark", () => {
try {
const src = $i.attr("src").trim(),
href = iCon.location.href;
if(href !== bl || src === bl || src === "") {
$i.off("load.jqueryMark");
callCallback();
}
} catch(e) {
errorFn();
}
});
};
if(iCon.document.readyState === compl) {
const src = $i.attr("src").trim(),
href = iCon.location.href;
if(href === bl && src !== bl && src !== "") {
observeOnload();
} else {
callCallback();
}
} else {
observeOnload();
}
} catch(e) { // accessing contentWindow failed
errorFn();
}
};
Working example
Consisting of two files (index.html and iframe.html):
index.html:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Parent</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-1.12.2.min.js"></script>
<script>
$(function() {
/**
* Will wait for an iframe to be ready
* for DOM manipulation. Just listening for
* the load event will only work if the iframe
* is not already loaded. If so, it is necessary
* to observe the readyState. The issue here is
* that Chrome will initialize iframes with
* "about:blank" and set its readyState to complete.
* So it is furthermore necessary to check if it's
* the readyState of the target document property.
* Errors that may occur when trying to access the iframe
* (Same-Origin-Policy) will be catched and the error
* function will be called.
* #param {jquery} $i - The jQuery iframe element
* #param {function} successFn - The callback on success. Will
* receive the jQuery contents of the iframe as a parameter
* #param {function} errorFn - The callback on error
*/
var onIframeReady = function($i, successFn, errorFn) {
try {
const iCon = $i.first()[0].contentWindow,
bl = "about:blank",
compl = "complete";
const callCallback = () => {
try {
const $con = $i.contents();
if($con.length === 0) { // https://git.io/vV8yU
throw new Error("iframe inaccessible");
}
successFn($con);
} catch(e) { // accessing contents failed
errorFn();
}
};
const observeOnload = () => {
$i.on("load.jqueryMark", () => {
try {
const src = $i.attr("src").trim(),
href = iCon.location.href;
if(href !== bl || src === bl || src === "") {
$i.off("load.jqueryMark");
callCallback();
}
} catch(e) {
errorFn();
}
});
};
if(iCon.document.readyState === compl) {
const src = $i.attr("src").trim(),
href = iCon.location.href;
if(href === bl && src !== bl && src !== "") {
observeOnload();
} else {
callCallback();
}
} else {
observeOnload();
}
} catch(e) { // accessing contentWindow failed
errorFn();
}
};
var $iframe = $("iframe");
onIframeReady($iframe, function($contents) {
console.log("Ready to got");
console.log($contents.find("*"));
}, function() {
console.log("Can not access iframe");
});
});
</script>
<iframe src="iframe.html"></iframe>
</body>
</html>
iframe.html:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Child</title>
</head>
<body>
<p>Lorem ipsum</p>
</body>
</html>
You can also change the src attribute inside index.html to e.g. "http://example.com/". Just play around with it.
I'd use postMessage. The iframe can assign its own onload event and post to the parent. If there are timing issues just make sure to assign the parent's postMessage handler before creating the iframe.
For this to work the iframe must know the url of the parent, for instance by passing a GET parameter to the iframe.
This function will run your callback function immediately if the iFrame is already loaded or wait until the iFrame is completely loaded before running your callback function.
Just pass in your callback function that you want to run when the iFrame finishes loading and the element to this function:
function iframeReady(callback, iframeElement) {
const iframeWindow = iframeElement.contentWindow;
if ((iframeElement.src == "about:blank" || (iframeElement.src != "about:blank" && iframeWindow.location.href != "about:blank")) && iframeWindow.document.readyState == "complete") {
callback();
} else {
iframeWindow.addEventListener("load", callback);
}
}
This will take care of the most common issues like chrome initializing iframe with about:blank and iFrame not supporting DOMContentLoaded event. See this https://stackoverflow.com/a/69694808/15757382 answer for explanation.
I had the same problem. In my case, I simply checked if the onload function is fired or not.
var iframe = document.getElementById("someIframe");
var loadingStatus = true;
iframe.onload = function () {
loadingStatus = false;
//do whatever you want [in my case I wants to trigger postMessage]
};
if (loadingStatus)
//do whatever you want [in my case I wants to trigger postMessage]
I tried very hard to come to a solution that worked consistently cross browser. IMPORTANT: I was not able to come to such a solution. But here is as far as I got:
// runs a function after an iframe node's content has loaded
// note, this almost certainly won't work for frames loaded from a different domain
// secondary note - this doesn't seem to work for chrome : (
// another note - doesn't seem to work for nodes created dynamically for some reason
function onReady(iframeNode, f) {
var windowDocument = iframeNode[0].contentWindow.document;
var iframeDocument = windowDocument?windowDocument : iframeNode[0].contentWindow.document;
if(iframeDocument.readyState === 'complete') {
f();
} else {
iframeNode.load(function() {
var i = setInterval(function() {
if(iframeDocument.readyState === 'complete') {
f();
clearInterval(i);
}
}, 10);
});
}
}
and I was using it like this:
onReady($("#theIframe"), function() {
try {
var context = modal[0].contentWindow;
var i = setInterval(function() {
if(context.Utils !== undefined && context.$) { // this mess is to attempt to get it to work in firefox
context.$(function() {
var modalHeight = context.someInnerJavascript();
clearInterval(i);
});
}
}, 10);
} catch(e) { // ignore
console.log(e);
}
});
Note that even this does not solve the problem for me. Here are some problems with this solution:
In onReady, for iframes that were added dynamically, iframeDocument.readyState seems to be stuck at "uninitialized" and thus the callback never fires
The whole setup still doesn't seem to work in firefox for some reason. It almost seems like the setInterval function is cleared externally.
Note that some of these problems only happen when there is a lot of other stuff loading on the page, which makes the timing of these things less deterministic.
So if anyone can improve upon this, it would be much appreciated.
Only when the content inside the iframe is loaded innerDoc is true and fires code inside the if.
window.onload = function(){
function manipulateIframe(iframeId, callback) {
var iframe = document.getElementById(iframeId).contentWindow.document;
callback(iframe);
};
manipulateIframe('IFwinEdit_forms_dr4r3_forms_1371601293572', function (iframe) {
console.log(iframe.body);
});};
example
I think you should try using onreadystatechange event.
http://jsfiddle.net/fk8fc/3/
$(function () {
var innerDoc = ($("#if")[0].contentDocument) ? $("#if")[0].contentDocument : $("#if")[0].contentWindow.document;
console.debug(innerDoc);
$("#if").load( function () {
alert("load");
alert(innerDoc.readyState)
});
innerDoc.onreadystatechange = function () {
alert(innerDoc.readyState)
};
setTimeout(innerDoc.onreadystatechange, 5000);
});
EDIT: the context is not what I think it is. you can just check the readyState of iframe document and everything should be fine.
OP: This is a packaged up function I made from the concepts described above:
// runs a function after an iframe node's content has loaded
// note, this almost certainly won't work for frames loaded from a different domain
onReady: function(iframeNode, f) {
var windowDocument = iframeNode[0].contentWindow.document;
var iframeDocument = windowDocument?windowDocument : iframeNode[0].contentWindow.document
if(iframeDocument.readyState === 'complete') {
f();
} else {
iframeNode.load(f);
}
}
I have the following handler:
$(window).bind('pageshow', function() { alert("back to page"); });
When I navigate away from the page (by pressing on a link) and return back to the page (by pressing the "back" button), the alert() is not called (IPad 2, iOS 5.1).
What am I doing wrong please? Any other event I need to bind to?
PS: interesting that pagehide is received properly when navigating away from the page.
You can check the persisted property of the pageshow event. It is set to false on initial page load. When page is loaded from cache it is set to true.
window.onpageshow = function(event) {
if (event.persisted) {
alert("back to page");
}
};
For some reason jQuery does not have this property in the event. You can find it from original event though.
$(window).bind("pageshow", function(event) {
if (event.originalEvent.persisted) {
alert("back to page");
}
};
This is likely a caching issue. When you go back to the page via the "back" button, the page is being pulled from the cache (behavior is dependent on the browser). Because of this, your JS will not fire since the page is already rendered in the cache and re-running your js could be detrimental to layout and such.
You should be able to overcome this by tweaking your caching headers in your response or using a handful of browser tricks.
Here are some links on the issue:
Is there a cross-browser onload event when clicking the back button?
After travelling back in Firefox history, JavaScript won't run
EDIT
These are all pulled from the above links:
history.navigationMode = 'compatible';
<body onunload=""><!-- This does the trick -->
"Firefox 1.5+ and some next version of Safari (which contains the fix for bug 28758) supports special events called pageshow and pagehide."
Using jQuery's $(document).ready(handler)
window.onunload = function(){};
What you're doing there is binding the return value of alert("back to page") as a callback. That won't work. You need to bind a function instead:
$(window).bind('pageshow', function() { alert("back to page"); });
I add the same problem where iOS does not always post the "pageshow" event when going back.
If not, safari resumes executing JS on the page so I though a timer would continue to fire.
So I came with this solution:
var timer;
function onPageBack() { alert("back to page"); }
window.addEventListener('pageshow', function() {
if (event.persisted)
onPageBack();
// avoid calling onPageBack twice if 'pageshow' event has been fired...
if (timer)
clearInterval(timer);
});
// when page is hidden, start timer that will fire when going back to the page...
window.addEventListener('pagehide', function() {
timer = setInterval(function() {
clearInterval(timer);
onPageBack();
}, 100);
});
I solved that issue like that;
$(window).bind("pageshow", function () {
setTimeout(function () {
back();
}, 1000);
});
function back() {
//YOUR CODES
}
you should checkout you page is has iFrame component? i dont know why , but i delete iFrame component to solve this question
The only way I got this to work across ALL web browsers is to disallow the caching of the page you are returning to. I know -- it is a bit radical; it would be nice to get it to work using JavaScript, but I could figure it out.
I added a Cache-Control header in my .asp pages a helpful list of most options available to add the header..
My test.asp page
<%#language="VBSCRIPT" CODEPAGE="65001" LCID=1033%>
<%
Option Explicit
SetLocale(1033)
Response.ContentType = "text/html"
Response.CharSet = "UTF-8"
Response.addHeader "Cache-Control", "no-cache, no-store, must-revalidate" ' HTTP 1.1.
Response.addHeader "Pragma", "no-cache" ' HTTP 1.0.
Response.addHeader "Expires", "0" ' Proxies.
%><html>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<style>
body {margin: 50px auto;text-align: center;}
a {padding: 5px;}
#spinner {width: 100%;height: 100%;background: green;position: absolute;top: 0;display:none;}
</style>
<head>
</head>
<body style="min-height: 100vh;">
Hello World!<br>
<h3 id="pagevalue"></h3>
page 1
page 2
page 3
<div id="spinner"></div>
<script>
function showSpinner(){
document.getElementById("spinner").style.display = "block";
}
var getUrlParameter = function getUrlParameter(sParam) {
var sPageURL = window.location.search.substring(1),
sURLVariables = sPageURL.split('&'),
sParameterName,
i;
for (i = 0; i < sURLVariables.length; i++) {
sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] === sParam) {
return sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]);
}
}
return false;
};
document.getElementById("pagevalue").innerHTML = "page "+getUrlParameter("page");
</script>
</body>
</html>
To complete your
alert("back to page")
request, I suggest you add a tracking mechanism that keeps track of the clicks.
I have the following function - it works fine in Firefox:
How I can I get this to fire on body resize in IE??
function sizeColumnHeadings() {
var $headingsTable = $('#TopicPostList_hdiv > table').eq(0),
$rowsTable = $('#TopicPostList_div > table').eq(0);
if ($headingsTable.length && $rowsTable.length) {
$headingsTable.width($rowsTable.width());
}
}
$(window).on('resize', function() {
sizeColumnHeadings();
}).trigger('resize');
If you use jquery, I found the following code works in IE, Firefox and Chrome.
var cnt = 0;
// this is called when the document finishes loading (similar to onLoad in the body tag)
$(function() {
// this asssigns a callback function each time the browser is resized
// it turns out that it is triggered multiple times on one resize
$(window).resize(function() {
cnt++;
// this code simply displays the # of times the callback is triggered
// I defined a <div id=show> in the body, and this updates its html.
$('#show').html('cnt = ' + cnt);
});
});
I recommend using jquery for this sort of problem, as it often takes care of the incompatibilities between different browser.
I have a simple frameset like
<frameset rows="100, 200">
<FRAME name="listener" src="frame1.html">
<FRAME name="content" src="http://www.someexternalurl.com/">
</frameset>
In the listener form trying to listen to the top window focus / blur:
<body>
<div id="szamlalo">30</div>
</body>
<script type="text/javascript">
var inFocus = true;
var counter = 30;
var timer;
$(document).ready(function(){
$(window.top).focus(function(){
//$(window.top).focusin(function(){
inFocus = true;
});
$(window.top).blur(function(){
//$(window.top).focusout(function(){
inFocus = false;
});
timer = window.setInterval("countdown()",1000);
});
function countdown()
{
if (counter > 0)
{
if (inFocus)
counter--;
}
else
{
window.clearInterval(timer);
window.location.href='masik_oldal.html';
}
$('#szamlalo').html(counter);
}
</script>
But aparently the major browsers all handles this differently, opera is the only one it works as expected. In IE the blur isn't received at all, chrome and firefox receive it only once...is this simply the different js engine implementation, or something in jquery? Or i made a mistake somewhere?
Thanks for any idea
Events do not bubble up the frameset tree. You need to install separate handlers for each window (frame)