I have the following code that works on Chrome and MS Edge but not on Firefox.
Parent.html has this script.
<html>
<body>
<script>
var ifr1 = document.createElement('iframe');
ifr1.onload = function() {
alert("iframe 1 loaded") //fires on all browsers
script = ifr1.contentWindow.document.createElement('script');
script.src = 'PATH/TO/script.js';
script.onload = function() {
alert("script 1 onload") //fires on all browsers
};
ifr1.contentWindow.document.body.appendChild(script);
};
document.body.appendChild(ifr1);
</script>
</body>
</html>
It creates an iframe and loads script.js within that iframe.
Here is script.js which does the same thing like above -
var ifr2 = document.createElement('iframe');
ifr2.onload = function() {
alert("iframe 2 loaded") //doesn't fire on Firefox
script = ifr2.contentWindow.document.createElement('script');
script.src = 'https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js';
script.onload = function() {
alert("script 2 onload")
};
ifr2.contentWindow.document.body.appendChild(script);
};
document.body.appendChild(ifr2);
It creates another iframe ifr2 within the iframe ifr1 created by Parent.html.
Now, Chrome and Edge show all the alerts properly but Firefox doesn't fire the onload event for ifr2 loaded within ifr1 (even IE fires the onload for ifr2). Any idea why?
A workaround is to create a simple html page - even just <html><head></head><body></body></html> (maybe even less) .. lets call it empty.html
if you now change Parent.html to
<html>
<body>
<script>
var ifr1 = document.createElement('iframe');
ifr1.onload = function() {
alert("iframe 1 loaded") //fires on all browsers
script = ifr1.contentWindow.document.createElement('script');
script.src = 'PATH/TO/script.js';
script.onload = function() {
alert("script 1 onload") //fires on all browsers
};
ifr1.contentWindow.document.body.appendChild(script);
};
ifr1.src = 'empty.html'; // add this code *******
document.body.appendChild(ifr1);
</script>
</body>
</html>
Then it works as expected
Not sure why firefox does what it does - it could be because without the src attribute, the iframe's location is about:blank - but why the first one works and not the second is a mystery to me
edit: well, it did once then stopped again!!
OK, really strange - if you add ifr1.src = 'empty.html' it works ... if you duplicate that in script.js it breaks again
Not sure I've answered you well, but at least I've given you a working kludge :p
IMO this is a bug in Firefox because Firefox doesn't execute the load event inside iframes the way you would expect. The developers over at Mozilla may disagree. It all comes down to your definition of what "load" should mean, and it's more complicated than you think.
In any case the real issue is when to know it's safe to access ifr2.contentWindow, and the whole point of using ifr2.onload the way you did is so that you can figure out when ifr2.contentWindow exists.
Note, however, that the contentWindow and document are created immediately when document.body.appendChild(ifr2) executes, so if you move the appendChild() call to above your current code and remove the onload function, it will work as expected.
Alternatively you can change the onload function to something else and execute it directly after appendChild(), as such:
var ifr2 = document.createElement('iframe');
function loadit() {
console.log("iframe 2 loaded")
script = ifr2.contentWindow.document.createElement('script');
script.src = '...';
script.onload = function() {
console.log("script 2 onload")
};
ifr2.contentWindow.document.body.appendChild(script);
};
// add the iframe to the document
document.body.appendChild(ifr2);
// now execute your script stuff:
loadit()
This should work across all (modern) browsers.
While this is a bad practice and deprecated in chrome (it will show a warning in console.
adding this line before apending script tag fix the issue for me:
iframe.contentWindow.document.write('<html><head></head><body></body></html>');
It appears firefox needs it to init iframe and run javascript...
Related
I was doing some javascript testing on chrome v71.0.3578.98, with the window.onload and I'm experiencing the js happening before the DOM loads
Ref gif: https://imgur.com/nxHYjRr
here is the code just wrapped in a simple html tag.
<h1>Title...</h1>
<p>lorem500...</p>
<script>
function pageLoad() {
alert('I\'m alive');
}
window.onload = pageLoad;
</script>
So in this basic page I tried running it in chrome v71 and the javascript ran first, but in firefox, opera it loaded after page loaded, as expected. Any thoughts?
The problem is that alert blocks - while an alert popup is visible, further page rendering is prevented, and when window.onload runs, the page may well not have rendered at all yet, especially if there isn't much HTML at all before it. All elements do exist in the document when the onload runs, they just may not be visible. (It depends on the browser)
alert is very user-unfriendly, as well as being difficult to work with (as you're encountering). Use console.log or a proper modal instead:
<h1>Title...</h1>
<p>lorem500...</p>
<script>
function pageLoad() {
console.log('I\'m alive');
}
window.onload = pageLoad;
</script>
If you had to use alert, only alert after an instant setTimeout, thus giving the browser a chance to paint the page before the alert gets called, just in case the browser hasn't rendered the page yet:
<h1>Title...</h1>
<p>lorem500...</p>
<script>
function pageLoad() {
setTimeout(() => {
alert('I\'m alive');
});
}
window.onload = pageLoad;
</script>
The below piece of code is not working in IE8, but, it is working perfectly on FireFox and Google Chorome, Even, there is no error thrown by IE8, but, output is not coming. Any Idea? What is the actual problem?
<html>
<head/>
<body>
<script type="text/javascript">
(function(){
var inpEle = document.createElement("div");
inpEle.setAttribute("id", "div1");
var texEle = document.createTextNode("This is my Sample Para. I am testing it again my own level that prove How i am capable of.");
inpEle.appendChild(texEle);
document.body.appendChild(inpEle);
})();
(function(){
var inpEle1 = document.createElement("input");
inpEle1.setAttribute("type", "button");inpEle1.setAttribute("value", "Show");inpEle1.setAttribute("onclick", "Show()");
document.body.appendChild(inpEle1);
var inpEle2 = document.createElement("input");
inpEle2.setAttribute("type", "button");inpEle2.setAttribute("value", "Hide");inpEle2.setAttribute("onclick", "Hide()");
document.body.appendChild(inpEle2);
})();
</script>
<script type="text/javascript">
window.onload= function(){
document.getElementById('div1').style.display="none";
}
Show = function (){
document.getElementById('div1').style.border="2pt solid green";
document.getElementById('div1').style.display="";
}
Hide = function(){
document.getElementById('div1').style.border="";
document.getElementById('div1').style.display="none";
}
</script>
</body>
</html>
You may not be able to use document.body.appendChild() in IE8 before the </body> tag has been parsed and your code is trying to append to the body while it is still being parsed. Early versions of IE like IE6 might just abort (e.g. literally crash) when you did this. Later versions (like IE8) will just simply ignore your request.
You can use document.write() to add content while the body is being parsed.
You can postpone calling your code until after the body has finished loading and parsing (such a <body onload="xxx()"> handler) or when an event such as window.onload fires.
You can appendChild() to an element that has finished parsing (something that is before your script).
The simplest solution is probably to put a <div id="container"></div> in the body before your script and append to that instead of the body or put your code in a function and have the body onload event call your function.
See this article for description of the issue.
don't use setattribute use anonymous function for events
inpEle1.onclick = function() {
Show();
};
Also it works in IE tester in IE8, but not IE7, do you have it using IE7 compatibility
Go to Internet Options - Security Tab - Internet - click on the "Custom" button then scroll down to the Miscellaneous section."Allow script-initiated windows without size or position constraints" Find "Active Scripting" then check enable.
I noticed than element.style.display doesn't always work with IE8.
Here is a method to improve compatibility :
if (element.style.setAttribute)
element.style.setAttribute("display", value);
else
element.style.display = value;
Best regards,
I haven't been able to make sense of the answers to related questions so far(down to my knowledge level), so...
I have a simple script(using jQuery) that opens a new window and adds certain content from the parent into a specified container inside the child. I'm not sure if it's my approach that's wrong or I'm just missing a step - the script to run on the new window runs in IE when it's outside of the window.onload function, but this breaks FF, and FF is happy when it's inside of the window.onload, but then the new window in IE doesn't appear to be doing anything(no alert, no add of content, nada).
Please can anybody explain to me why this is the case/what I'm doing wrong? Is it something to do with the reference to window.open?
This is the script:
var printPage = function(container){
$('.printButton').click(function(){
var printWindow = window.open('printWindow.html');
var contentFromParent = $(container).eq(0).html();
/*works for IE, but not FF
printWindow.alert('works for IE, but not FF');
printWindow.document.getElementById('wrap').innerHTML = contentFromParent;*/
/*works for FF and Chrome but not IE:*/
printWindow.onload = function(){
printWindow.alert('works for FF and Chrome but not IE');
printWindow.document.getElementById('wrap').innerHTML = contentFromParent;
}
/*I also tried:
$(printWindow.document).ready(function(){
printWindow.alert('load the page, fill the div');
printWindow.document.getElementById('wrap').innerHTML = contentFromParent;
}); //works for IE, not working for FF/Chrome*/
})
}
printPage('#printableDiv');
The HTML:
<div id="wrap">
<button href="#" class="printButton">Print</button>
<div id="printableDiv">
<p>I want to see this content in my new window please</p>
</div>
</div>
UPDATE
Thanks for your pointers about onload in the new window - I've gone with this solution for now: Setting OnLoad event for newly opened window in IE6 - simply checking the DOM and delaying the onload - working for IE7/8/9.
I'm not sure if you'd call it an 'elegant' solution, but it's working! Further comments, especially if you think this is flawed, would be appreciated. Thanks.
var newWinBody;
function ieLoaded(){
newWinBody = printWindow.document.getElementsByTagName('body');
if (newWinBody[0]==null){
//page not yet ready
setTimeout(ieLoaded, 10);
} else {
printWindow.onload = function(){
printWindow.alert('now working for all?');
printWindow.document.getElementById('wrap').innerHTML = contentFromParent;
}
}
}
IEloaded();
Can it be that the page you open fires the 'onload' event before you set the event handler printWindow.onload = ... ?
You might consider including some javascript in your 'printWindow.html' page. Let's say you add a short <script>var printWindowLoaded = true;</script> at the end of your page. Then your main script would do something like this:
function doStuff() {
//...
}
if (printWindow.printWindowLoaded)
doStuff();
else
printWindow.onload = doStuff;
This is my code
<script>
var body = "dddddd"
var script = "<script>window.print();</scr'+'ipt>";
var newWin = $("#printf")[0].contentWindow.document;
newWin.open();
newWin.close();
$("body",newWin).append(body+script);
</script>
<iframe id="printf"></iframe>
This works but it prints the parent page, how do I get it to print just the iframe?
I would not expect that to work
try instead
window.frames["printf"].focus();
window.frames["printf"].print();
and use
<iframe id="printf" name="printf"></iframe>
Alternatively try good old
var newWin = window.frames["printf"];
newWin.document.write('<body onload="window.print()">dddd</body>');
newWin.document.close();
if jQuery cannot hack it
Live Demo
document.getElementById("printf").contentWindow.print();
Same origin policy applies.
Easy way (tested on ie7+, firefox, Chrome,safari ) would be this
//id is the id of the iframe
function printFrame(id) {
var frm = document.getElementById(id).contentWindow;
frm.focus();// focus on contentWindow is needed on some ie versions
frm.print();
return false;
}
an alternate option, which may or may not be suitable, but cleaner if it is:
If you always want to just print the iframe from the page, you can have a separate "#media print{}" stylesheet that hides everything besides the iframe. Then you can just print the page normally.
You can use this command:
document.getElementById('iframeid').contentWindow.print();
This command basically is the same as window.print(), but as the window we would like to print is in the iframe, we first need to obtain an instance of that window as a javascript object.
So, in reference to that iframe, we first obtain the iframe by using it's id, and then it's contentWindow returns a window(DOM) object. So, we are able to directly use the window.print() function on this object.
I had issues with all of the above solutions in IE8, have found a decent workaround that is tested in IE 8+9, Chrome, Safari and Firefox. For my situation i needed to print a report that was generated dynamically:
// create content of iframe
var content = '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">'+
'<head><link href="/css/print.css" media="all" rel="stylesheet" type="text/css"></head>'+
'<body>(rest of body content)'+
'<script type="text/javascript">function printPage() { window.focus(); window.print();return; }</script>'+
'</body></html>';
Note the printPage() javascript method before the body close tag.
Next create the iframe and append it to the parent body so its contentWindow is available:
var newIframe = document.createElement('iframe');
newIframe.width = '0';
newIframe.height = '0';
newIframe.src = 'about:blank';
document.body.appendChild(newIframe);
Next set the content:
newIframe.contentWindow.contents = content;
newIframe.src = 'javascript:window["contents"]';
Here we are setting the dynamic content variable to the iframe's window object then invoking it via the javascript: scheme.
Finally to print; focus the iframe and call the javascript printPage() function within the iframe content:
newIframe.focus();
setTimeout(function() {
newIframe.contentWindow.printPage();
}, 200);
return;
The setTimeout is not necessarily needed, however if you're loading large amounts of content i found Chrome occasionally failed to print without it so this step is recommended. The alternative is to wrap 'newIframe.contentWindow.printPage();' in a try catch and place the setTimeout wrapped version in the catch block.
Hope this helps someone as i spent a lot of time finding a solution that worked well across multiple browsers. Thanks to SpareCycles.
EDIT:
Instead of using setTimeout to call the printPage function use the following:
newIframe.onload = function() {
newIframe.contentWindow.printPage();
}
At this time, there is no need for the script tag inside the iframe. This works for me (tested in Chrome, Firefox, IE11 and node-webkit 0.12):
<script>
window.onload = function() {
var body = 'dddddd';
var newWin = document.getElementById('printf').contentWindow;
newWin.document.write(body);
newWin.document.close(); //important!
newWin.focus(); //IE fix
newWin.print();
}
</script>
<iframe id="printf"></iframe>
Thanks to all answers, save my day.
If you are setting the contents of IFrame using javascript document.write() then you must close the document by newWin.document.close(); otherwise the following code will not work and print will print the contents of whole page instead of only the IFrame contents.
var frm = document.getElementById(id).contentWindow;
frm.focus();// focus on contentWindow is needed on some ie versions
frm.print();
I was stuck trying to implement this in typescript, all of the above would not work. I had to first cast the element in order for typescript to have access to the contentWindow.
let iframe = document.getElementById('frameId') as HTMLIFrameElement;
iframe.contentWindow.print();
Use this code for IE9 and above:
window.frames["printf"].focus();
window.frames["printf"].print();
For IE8:
window.frames[0].focus();
window.frames[0].print();
I am wondering what's your purpose of doing the iframe print.
I met a similar problem a moment ago: use chrome's print preview to generate a PDF file of a iframe.
Finally I solved my problem with a trick:
$('#print').click(function() {
$('#noniframe').hide(); // hide other elements
window.print(); // now, only the iframe left
$('#noniframe').show(); // show other elements again.
});
Is there a way to capture when the contents of an iframe have fully loaded from the parent page?
<iframe> elements have a load event for that.
How you listen to that event is up to you, but generally the best way is to:
1) create your iframe programatically
It makes sure your load listener is always called by attaching it before the iframe starts loading.
<script>
var iframe = document.createElement('iframe');
iframe.onload = function() { alert('myframe is loaded'); }; // before setting 'src'
iframe.src = '...';
document.body.appendChild(iframe); // add it to wherever you need it in the document
</script>
2) inline javascript, is another way that you can use inside your HTML markup.
<script>
function onMyFrameLoad() {
alert('myframe is loaded');
};
</script>
<iframe id="myframe" src="..." onload="onMyFrameLoad(this)"></iframe>
3) You may also attach the event listener after the element, inside a <script> tag, but keep in mind that in this case, there is a slight chance that the iframe is already loaded by the time you get to adding your listener. Therefore it's possible that it will not be called (e.g. if the iframe is very very fast, or coming from cache).
<iframe id="myframe" src="..."></iframe>
<script>
document.getElementById('myframe').onload = function() {
alert('myframe is loaded');
};
</script>
Also see my other answer about which elements can also fire this type of load event
Neither of the above answers worked for me, however this did
UPDATE:
As #doppleganger pointed out below, load is gone as of jQuery 3.0, so here's an updated version that uses on. Please note this will actually work on jQuery 1.7+, so you can implement it this way even if you're not on jQuery 3.0 yet.
$('iframe').on('load', function() {
// do stuff
});
There is another consistent way (only for IE9+) in vanilla JavaScript for this:
const iframe = document.getElementById('iframe');
const handleLoad = () => console.log('loaded');
iframe.addEventListener('load', handleLoad, true)
And if you're interested in Observables this does the trick:
import { fromEvent } from 'rxjs';
const iframe = document.getElementById('iframe');
fromEvent(iframe, 'load').subscribe(() => console.log('loaded');
Note that the onload event doesn't seem to fire if the iframe is loaded when offscreen. This frequently occurs when using "Open in New Window" /w tabs.
Step 1: Add iframe in template.
<iframe id="uvIFrame" src="www.google.com"></iframe>
Step 2: Add load listener in Controller.
document.querySelector('iframe#uvIFrame').addEventListener('load', function () {
$scope.loading = false;
$scope.$apply();
});
You can also capture jquery ready event this way:
$('#iframeid').ready(function () {
//Everything you need.
});
Here is a working example:
http://jsfiddle.net/ZrFzF/