Edge & IE: Permission Denied after creating an iframe and accessing window object - javascript

I'm getting the "Permission Denied" error on Edge and IE when creating an iframe in the script with some delay (setTimeout) and trying to access the window object.
It works on all other browsers, and works without the delay on IE and Edge.
<script>
function createIframe(win) {
console.log('foo', win.foo);
var width = 300;
var height = 250;
var id = 'ifr';
var src = '';
var iframeTemplate = '<iframe id="'+id+'" src="'+src+'" width="'+width+'" height="'+
height+'" marginheight="0" marginwidth="0" scrolling="NO" frameborder="0"></iframe>';
win.document.write(iframeTemplate);
var iframe = win.document.getElementById(id);
console.log('foo', win.foo);
if (iframe) {
var doc = iframe.contentWindow.document;
doc.write('<html><head></head><body><script>console.log("foo on parent", window.parent.foo);</scr'+ 'ipt></body></html>');
doc.close();
} else {
console.log('Failed to create an iframe');
}
}
window.foo = 'bar';
setTimeout(function() {
createIframe(window);
}, 3000);
</script>
This code should print:
foo bar
foo bar
foo on parent bar
But it throws the error "Permission Denied" on the second console.log on Edge and IE.
Works fine without setTimeout.
If I remove the second console.log, and acess window.parent.foo from within an iframe, it is undefined on Edge and IE
Snippets:
Doesn't work on Edge and IE: https://jsfiddle.net/vo2yrjft/
Works on Edge and IE: https://jsfiddle.net/6cbfk1yr/
Any workaround for that?

document.write is a "bad practice", it will block the page, you could refer to this thread for detailed information.
As a workaround, you could use createElement('iframe') to create an iframe and then use appendChild() to insert the element. Below is the sample code, it can run well in IE & Edge:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<script>
function prepareFrame(win) {
console.log('foo', win.foo);
var id = 'ifr';
var ifrm = document.createElement('iframe');
ifrm.setAttribute('src', '');
ifrm.setAttribute('id', id);
ifrm.setAttribute('marginheight', 0);
ifrm.setAttribute('marginwidth', 0);
ifrm.setAttribute('scrolling', 'NO');
ifrm.setAttribute('frameborder', 0);
ifrm.style.width = '300px';
ifrm.style.height = '250px';
document.body.appendChild(ifrm);
var iframe = win.document.getElementById(id);
console.log('foo', win.foo);
if (iframe) {
var doc = iframe.contentWindow.document;
doc.write('<html><head></head><body><script>console.log("foo on parent", window.parent.foo);</scr' + 'ipt></body></html>');
doc.close();
} else {
console.log('Failed to create an iframe');
}
}
window.foo = 'bar';
setTimeout(function() {
prepareFrame(window);
}, 3000);
</script>
</body>
</html>

Related

Javascript create iframe set content then return it from one function

I need to create an iframe dynamically, set its html, then return it as a function so it can be called later with newAdUnit(). Right now it returns [object HTMLIFrameElement]. I'm trying to figure out a way to do this all from one function. The reason for this is I'm setting up ads that need to be loaded in dynamically. A single function would make my code a lot cleaner since I can call it in a number of different ways. Any ideas?
<script>
function newAdUnit(size) {
var iframe = document.createElement('iframe');
iframe.onload = function() {
iframe = iframe.contentWindow.document;
var html = '<body>This is a test</body>';
iframe.open();
iframe.write(html);
iframe.close();
};
return iframe;
}
</script>
<div id="test">
<script>document.getElementById("test").innerHTML = newAdUnit()</script>
</div>
It will be so much easy if you use JQuery for the current operation. The code below shows how to
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
function newAdUnit(obj) {
var iframe = document.createElement('iframe');
iframe.onload = function() {
iframe = iframe.contentWindow.document;
var html = '<body>This is a test</body>';
iframe.open();
iframe.write(html);
iframe.close();
};
$(obj).append(iframe);
}
newAdUnit($('#test'));
});
</script>
</head>
<body>
<div id="test">
</div>
</body>
</html>
You should only use .innerHTML when the thing you want to add is a string of HTML. However, in your case, you have a HTMLIFrameElement object, and so, you can't use .innerHTML in this case. Currently, Javascript is implicitly calling .toString() on your element object returned by newAdUnit(), which results in [object HTMLIFrameElement].
Instead, when you want to add a node element to another element, you can use .appendChild() like so:
<div id="test"></div>
<script>
function newAdUnit(size) {
var iframe = document.createElement('iframe');
iframe.onload = function() {
iframe = iframe.contentWindow.document;
var html = '<body>This is a test. The size is ' + size + '</body>';
iframe.open();
iframe.write(html);
iframe.close();
};
return iframe;
}
document.getElementById("test").appendChild(newAdUnit(10));
</script>

Chrome print blank page

I have an old javascript code to print images, if a user clicks on the thumbnail. It used to work just fine, but lately (only in Chrome!) there is a blank page in preview.
Here is a demonstration in JsBin: http://jsbin.com/yehefuwaso/7
Click the printer icon. Now try it in Firefox; it will work as expected.
Chrome: 41.0.2272.89 m
Firefox: 30.0, 36.0.1
function newWindow(src){
win = window.open("","","width=600,height=600");
var doc = win.document;
// init head
var head = doc.getElementsByTagName("head")[0];
// create title
var title = doc.createElement("title");
title.text = "Child Window";
head.appendChild(title);
// create script
var code = "function printFunction() { window.focus(); window.print(); }";
var script = doc.createElement("script");
script.text = code;
script.type = "text/javascript";
head.appendChild(script);
// init body
var body = doc.body;
//image
doc.write('<img src="'+src+'" width="300">');
//chrome
if (navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
win.printFunction();
} else {
win.document.close();
win.focus();
win.print();
win.close();
}
}
It looks like it's attempting to print before the <img> has loaded, move the call to print inside an event handler for the load event of window by opening the link as a data URI or Blob, for example
var code = '\
<html>\
<head>\
<title></title>\
<script>\
function printFunction() {\
window.focus();\
window.print();\
window.close();\
}\
window.addEventListener(\'load\', printFunction);\
</script>\
</head>\
<body><img src="'+src+'" width="300"></body>\
</html>';
window.open('data:text/html,' + code, '_blank', 'width=600,height=600');
Don't forget you may need to HTML encode the tags in code
You could probably just listen for load on the <img> instead, but if you ever do anything more complicated than tring to print a single image you may find it breaks again in future
doc.write('<img onload="printFunction();" src="'+src+'" width="300">');
Where printFunction is the print function for all browsers
I encountered the same problem in Chrome. You can try these approaches, the first one worked for me. setTimeout didn't work for me (had to edit this later).
function printDiv(divName) {
var printContents = document.getElementById(divName).innerHTML;
w = window.open();
w.document.write(printContents);
w.document.write('<scr' + 'ipt type="text/javascript">' + 'window.onload = function() { window.print(); window.close(); };' + '</sc' + 'ript>');
w.document.close(); // necessary for IE >= 10
w.focus(); // necessary for IE >= 10
return true;
}
setTimeout:
<div id="printableArea">
<h1>Print me</h1>
</div>
<input type="button" onclick="printDiv('printableArea')" value="print a div!" />
function printDiv(divName) {
var printContents = document.getElementById(divName).innerHTML;
w = window.open();
w.document.write(printContents);
w.document.close(); // necessary for IE >= 10
w.focus(); // necessary for IE >= 10
setTimeout(function () { // necessary for Chrome
w.print();
w.close();
}, 0);
return true;
}
You need to wait for the image to finish loading:
var img = new Image();
img.src = src;
img.style.width = '300px';
img.onload = function(){
doc.write(img);
};

Cannot call method 'appendChild' of null for iframes

I have the following script to create an iframe
function createIframe() {
var iframe = document.createElement("iframe");
iframe.style.position = "absolute";
iframe.style.visibility = "hidden";
document.body.appendChild(iframe);
// Firefox, Opera
if(iframe.contentDocument) iframe.doc = iframe.contentDocument;
// Internet Explorer
else if(iframe.contentWindow) iframe.doc = iframe.contentWindow.document;
// Magic: Force creation of the body (which is null by default in IE).
// Also force the styles of visited/not-visted links.
iframe.doc.open();
iframe.doc.write('<style>');
iframe.doc.write("a{color: #000000; display:none;}");
iframe.doc.write("a:visited {color: #FF0000; display:inline;}");
iframe.doc.write('</style>');
iframe.doc.close();
// Return the iframe: iframe.doc contains the iframe.
return iframe;
}
however in chrome console it is giving me the error Cannot call method 'appendChild' of null
why is it not working?
The code is correct. Have you added body in the page?
Try this:
<html>
<body>
<script>
window.onload=function() {
createIframe()
};
function createIframe() {
var iframe = document.createElement("iframe");
iframe.style.position = "absolute";
iframe.style.visibility = "hidden";
document.body.appendChild(iframe);
// Firefox, Opera
if(iframe.contentDocument) iframe.doc = iframe.contentDocument;
// Internet Explorer
else if(iframe.contentWindow) iframe.doc = iframe.contentWindow.document;
// Magic: Force creation of the body (which is null by default in IE).
// Also force the styles of visited/not-visted links.
iframe.doc.open();
iframe.doc.write('<style>');
iframe.doc.write("a{color: #000000; display:none;}");
iframe.doc.write("a:visited {color: #FF0000; display:inline;}");
iframe.doc.write('</style>');
iframe.doc.close();
// Return the iframe: iframe.doc contains the iframe.
return iframe;
}
</script>
</body>
</html>
iframe.doc.write is not the cleanest solution....

Javascript method "undefined" when it is already?

I have the following code:
<script language="JavaScript" type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
function googleJS1(){
var iframe = document.getElementsByTagName('iframe')[0];
var doc = iframe.contentWindow.document;
var newScript = doc.createElement('div');
newScript.setAttribute("id", "google_translate_element");
var bodyClass = doc.getElementsByTagName('body')[0];
bodyClass.insertBefore(newScript, bodyClass.childNodes[0]);
}
function googleJS2(){
var iframe = document.getElementsByTagName('iframe')[0];
var doc = iframe.contentWindow.document;
var newScript = doc.createElement('script');
newScript.setAttribute("src", "http://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit");
var bodyClass = doc.getElementsByTagName('head')[0];
bodyClass.insertBefore(newScript, bodyClass.childNodes[0]);
}
function googleJS3(){
var iframe = document.getElementsByTagName('iframe')[0];
var doc = iframe.contentWindow.document;
var newScript = doc.createElement('script');
newScript.setAttribute("src", "http://www.mydomain.com/google.js");
var bodyClass = doc.getElementsByTagName('head')[0];
bodyClass.insertBefore(newScript, bodyClass.childNodes[1]);
}
function googleJS4(){
var iframe = document.getElementsByTagName('iframe')[0];
var doc = iframe.contentWindow.document;
var newScript = doc.createElement('style');
var content = doc.createTextNode('.goog-te-banner-frame { display: none; } #google_translate_element {}');
newScript.appendChild(content);
var bodyClass = doc.getElementsByTagName('head')[0];
bodyClass.insertBefore(newScript, bodyClass.childNodes[2]);
}
</script>
<iframe width=100% height= 100% onload ="googleJS1(); googleJS2(); googleJS3(); googleJS4();" class=iframe2 src="www.mydomain.com/test.html">
This code works fine on another server of mine elsewhere. However whenever this is run the console says "Uncaught referencer error, method undefined". Even though it is defined, why does it throw this message and how do I solve it?
Thanks
I haven't tested, but I'm assuming this is because the onload event is attached to the iframe, and will therefore be called inside the iframe's scope. Try calling window.parent.googleJS1() instead.

Unable to trigger event in IE during cloning

Following is the code which will clone a set of div with their events (onclick) which is working fine for Firefox but in case of IE it is not firing events associated with each div:
<html>
<head>
<style type='text/css'>
.firstdiv{
border:1px solid red;
}
</style>
<script language="JavaScript">
function show_tooltip(idx,condition,ev) {
alert(idx +"=="+condition+"=="+ev);
}
function createCloneNode () {
var cloneObj = document.getElementById("firstdiv").cloneNode(true);
document.getElementById("maindiv").appendChild(cloneObj);
}
function init(){
var mainDiv = document.createElement("div");
mainDiv.id = 'maindiv';
var firstDiv = document.createElement("div");
firstDiv.id ='firstdiv';
firstDiv.className ='firstdiv';
for(var j=0;j<4;j++) {
var summaryDiv = document.createElement("div");
summaryDiv.id = "sDiv"+j
summaryDiv.className ='summaryDiv';
summaryDiv.onmouseover = function() {this.setAttribute("style","text-decoration:underline;cursor:pointer;");}
summaryDiv.onmouseout = function() {this.setAttribute("style","text-decoration:none;");}
summaryDiv.setAttribute("onclick", "show_tooltip("+j+",'view_month',event)");
summaryDiv.innerHTML = 'Div'+j;
firstDiv.appendChild(summaryDiv);
}
mainDiv.appendChild(firstDiv);
var secondDiv = document.createElement("div");
var linkDiv = document.createElement("div");
linkDiv.innerHTML ='create clone of above element';
linkDiv.onclick = function() {
createCloneNode();
}
secondDiv.appendChild(linkDiv);
mainDiv.appendChild(secondDiv);
document.body.appendChild(mainDiv);
}
</script>
</head>
<body>
<script language="JavaScript">
init()
</script>
</body>
</html>
Can anybody tell me what's the problem in above code? Please correct me.
You have multiple problems with your code that make it either not working in some browsers or partial working in others:
onmouseover/onmouseout event
handlers assigned as properties do
not and shall not be copyied when
cloning (in any browser according to DOM specification), that is why you do not see
text-underline effect in any browser
In Internet Explorer (prior to IE9) it is not possible to assign an event handler by setting a onxxx attribute with setAttribute method
You clone an HTML structure with id attributes and insert it into the same document which creates a problem of duplicate id's - this is "illegal" and can lead to unpredictable behavior
So the only solution for you code to start working properly in every browser is to clone fragment without ids and (re-)assign event handlers manually.
I agree with #Sergey Ilinsky. You're running head first into DOM differences between IE and FF.
Try this code, it should help.
<html>
<head>
<style type='text/css'>
.firstdiv{
border:1px solid red;
}
</style>
<script language="JavaScript">
var cloneCount = 0;
var bname = navigator.appName;
var isIE = false;
if (bname == "Microsoft Internet Explorer"){
isIE = true;
}
else{
isIE = false;
}
function show_tooltip(idx,condition,ev) {
alert(idx +"=="+condition+"=="+ev);
}
function createCloneNode () {
var cloneObj = document.getElementById("firstdiv").cloneNode(false);
cloneObj.id += cloneCount++;
createSummaryNodes(cloneObj);
document.getElementById("maindiv").appendChild(cloneObj);
}
function createSummaryNodes(firstDiv){
for(var j=0;j<4;j++) {
var summaryDiv = document.createElement("div");
summaryDiv.id = firstDiv.id+"sDiv"+j
summaryDiv.className ='summaryDiv';
if(isIE){
summaryDiv.onmouseover = function() {this.style.textDecoration="underline";this.style.cursor="pointer";}
summaryDiv.onmouseout = function() {this.style.textDecoration="none";}
summaryDiv.onclick = function() { show_tooltip(j,'view_month',event); };
}
else{
summaryDiv.onmouseover = function() {this.setAttribute("style","text-decoration:underline;cursor:pointer;");}
summaryDiv.onmouseout = function() {this.setAttribute("style","text-decoration:none;");}
summaryDiv.setAttribute("onclick", "show_tooltip("+j+",'view_month',event)");
}
summaryDiv.innerHTML = 'Div'+j;
firstDiv.appendChild(summaryDiv);
}
}
function init(){
var mainDiv = document.createElement("div");
mainDiv.id = 'maindiv';
var firstDiv = document.createElement("div");
firstDiv.id ='firstdiv';
firstDiv.className ='firstdiv';
createSummaryNodes(firstDiv);
mainDiv.appendChild(firstDiv);
var secondDiv = document.createElement("div");
var linkDiv = document.createElement("div");
linkDiv.innerHTML ='create clone of above element';
linkDiv.onclick = function() {
createCloneNode();
}
secondDiv.appendChild(linkDiv);
mainDiv.appendChild(secondDiv);
document.body.appendChild(mainDiv);
}
</script>
</head>
<body>
<script language="JavaScript">
init();
</script>
</body>
</html>
EDIT: I added some VERY basic browser detection, took out the deep copy with cloneNode, restructured some of the code, and added some browser specific code.

Categories