Problem: Javascript iframe send the postMessage() before iframe completed loading - javascript

I have iframe in parent.html. Child.html sending the postMessage('documnent.cookie','*') to the parent window.
The problem is postMessage() send 'null'. postMessage() is triggered before iframe loading is completely done. I have to postMessage() to Parent window only if iframe completely loads the data.
Here is my code: parent.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<body id="Body" style="background-color:Silver">
<iframe id ="CA_FRAME_0" src="http://localhost/ibmcognos/bi/child.html" style="display:none"></iframe>
<script type="text/javascript">
window.addEventListener("message", function (e){
if(e.data == null){
alert('fail');
}else{
alert('sucess');
document.cookie="key=" +e.data + ";expires=0;path=/";
}
}, false);
</script>
<div id="arcContainer" class="arcContainer"></div>
<script type="text/javascript">
ARCPLAN.language = "XXX";
ARCPLAN.application = "lv02";
ARCPLAN.startDocName = "application.apa";
ARCPLAN.arcCgiSite = "http://localhost/.....";
</script>
</body>
</html>
//child.html
<!DOCTYPE html> <html> <head>
<title>COOKIE</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> </head> <body>
<script type="text/javascript">
function getCookie(name) { var arg = name + "="; var alen = arg.length; var clen = document.cookie.length; var i = 0; while(i < clen) { var j = i
+ alen; if(document.cookie.substring(i, j) == arg)
return getCookieVal(j); i = document.cookie.indexOf(" ", i) + 1; if(i == 0)
break; } return null; }
function getCookieVal(offset) { var endstr = document.cookie.indexOf(";", offset); if(endstr == -1) endstr = document.cookie.length; return document.cookie.substring(offset, endstr); }
**parent.postMessage(getCookie("key"), "*");** </script>
**<iframe id ="CA_FRAME" src="http://localhost/ibmcognos/bi/" style="display: none;" ></iframe>** - *this url make the redirect from here and set the cookie, it takes time*
</body> </html>
Kindly provide me some suggestions. Thanks.

In this case we have a webpage A need to load another webpage B through iframe.
page A is loaded. we can add a custom event to listen page B load event.
page A code:
const iframeWin = document.getElementById('h5-iframe').contentWindow;
window.addEventListener(
'message',
(e) => {
const { data } = e;
console.log('receive page load', data);
if (data.pageLoaded) {
iframeWin.postMessage(youdata, '*');
}
},
false,
);
page B code:
window.addEventListener(
'message',
(e) => {
console.log('receive youdata', data);
const { data } = e;
},
false,
);
// after register listen function send this message to page A.
window.parent.postMessage({ pageLoaded: true }, '*');

Add an onload event to your iFrame:
<iframe id ="CA_FRAME" src="http://localhost/ibmcognos/bi/" onload="onCaFrameLoad(this)" style="display: none;"></iframe>
Then you can run JS code only after the frame has completely loaded:
function onCaFrameLoad() {
// wrap your post in this function
};
This way, the code will only run once the frame has finished loading.
PoC:
parent.html
<html>
<body>
PARENT- I get data from child
<br /><br />
<iframe id ="CA_FRAME_0" src="child.html"></iframe>
</body>
</html>
child.html
<html>
<body>
CHILD - I send data to parent - but only after CA_FRAME has loaded
<br /><br />
<iframe id ="CA_FRAME" src="inner.html" onload="onCaFrameLoad(this)"></iframe>
<script>
function onCaFrameLoad() {
alert('iframe has loaded! now you can send data to the parent');
};
</script>
</body>
</html>
inner.html
<html>
<body>
INNER - I load all my content, then the onCaFrameLoad function in child.html runs
<script>
alert('My Name is INNER - I load all my content first');
</script>
</body>
</html>

Related

Chrome not reloading object tag after linking to nonexistent file

I have the following index.html. The objective of the javascript below is to reload the #obj element's data tag, so that it can display multiple images. However, it is possible that one of the images I link the buttons to doesn't exist (in this case, #2).
function updateObject(evt) {
var id = evt.currentTarget.id;
var object = document.getElementById("obj");
if (id == "1") {
object.setAttribute("data","https://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_logo_black.svg")
}
else {
object.setAttribute("data", "file/that/doesnt/exist")
}
}
for (var i = 0; i < document.getElementsByTagName("button").length; i++) {
document.getElementsByTagName("button")[i].addEventListener("click", updateObject, false);
}
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
</head>
<body>
<button id="1">button1</button>
<button id="2">button2</button>
<object id="obj" style='width: 100px'></object>
</body>
</html>
What I expect to happen in the following script is this:
The user presses button1, sees apple
User presses button2, sees nothing
User presses button1, sees apple
However, the third step in that doesn't happen - when I try to reload the object's data after linking to a nonexistent file, it stays blank.
As far as I've been able to gather, this happens in Chrome, and for me works in Safari. I must use the object tag, or some other method that allows for interactive SVG.
One solution you could possibily do is to remove and add the node itself to force a hard reset
var clone = object.cloneNode();
var parent = object.parentNode;
parent.removeChild(object);
parent.appendChild(clone);
function updateObject(evt) {
var id = evt.currentTarget.id;
var object = document.getElementById("obj");
if (id == "1") {
object.setAttribute("data", "https://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_logo_black.svg")
var clone = object.cloneNode();
var parent = object.parentNode;
parent.removeChild(object);
parent.appendChild(clone);
} else {
object.setAttribute("data", "file/that/doesnt/exist")
}
}
for (var i = 0; i < document.getElementsByTagName("button").length; i++) {
document.getElementsByTagName("button")[i].addEventListener("click", updateObject, false);
}
<button id="1">button1</button>
<button id="2">button2</button>
<object id="obj" style='width: 100px'></object>
Try changing the tag to an <img> and setting the "src" attribute.
function updateObject(evt) {
var id = evt.currentTarget.id;
var object = document.getElementById("obj");
if (id == "1") {
object.setAttribute("src","https://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_logo_black.svg")
}
else {
object.setAttribute("src", "file/that/doesnt/exist")
}
}
for (var i = 0; i < document.getElementsByTagName("button").length; i++) {
document.getElementsByTagName("button")[i].addEventListener("click", updateObject, false);
}
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
</head>
<body>
<button id="1">button1</button>
<button id="2">button2</button>
<img id="obj" style='width: 100px'></img>
</body>
</html>
I provide a sample which helps you to solve your problem by making a fake request to that URL.
Chrome does it to inform. Even if you handle onerror correctly with correct error handling with try-catch and every trick with a void or ( ) that is told to prevent error - you can not fix it. It is out of Javascript control.
function updateObject(evt) {
var id = evt.currentTarget.id;
var object = document.getElementById("obj");
if (id == "1") {
object.setAttribute("data","https://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_logo_black.svg");
}
else {
var request;
if(window.XMLHttpRequest)
request = new XMLHttpRequest();
else
request = new ActiveXObject("Microsoft.XMLHTTP");
request.open('GET', 'file/that/doesnt/exist', false);
request.send();
// the object request will be actually modified
if (request.status === 404) {
alert("The file you are trying to reach is not available.");
}
else
{
object.setAttribute("data", "file/that/doesnt/exist");
}
}
}
for (var i = 0; i < document.getElementsByTagName("button").length; i++) {
document.getElementsByTagName("button")[i].addEventListener("click", updateObject, false);
}
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
</head>
<body>
<button id="1">button1</button>
<button id="2">button2</button>
<object id="obj" style='width: 100px'></object>
</body>
</html>
But notice that it will only work on the same origin. For another host, you will have to use a server-side language to do that, which you will have to figure it out by yourself.

postMessage from an iframe opened in in new window

Please i have a small form that i open on a popop window like below.
<html>
<head>
</head>
<body>
<div id="popUpDiv" style="display:none;"></div>
<script type="text/javascript">
var popUpWindow;
function popup(n) {
popUpWindow = window.open("",n, "height=500,width=500");
}
function foo(obj){
test1 = "http://localhost:3001";
popUpWindow.document.write('<iframe height="450" id="receiver" allowTransparency="true" frameborder="0" scrolling="yes" style="width:100%;" src="'+test1+'" type= "text/javascript"></iframe>');
}
</script>
OnSale
</body>
</html>
above is the parent form and is running on the server localhost:3000. I opened new window with an iframe on it. I intend to postMessage from the popup or child window and receive it in the parent window. I then modified my code adding the onload below.
window.onload = function() {.
var messageEle = document.getElementById('message');
console.log('its receiving message');
function receiveMessage(e) {
alert('received');
console.log('the origin is ', e.origin);
if (e.origin !== "http://localhost:3001"){
return;
}
messageEle.innerHTML = "Message Received: " + e.data;
}
window.addEventListener('message', receiveMessage);
}
my complete parent page now looks like below.
<html>
<head>
</head>
<body>
<div id="popUpDiv" style="display:none;"></div>
<script type="text/javascript">
var popUpWindow;
function popup(n) {
popUpWindow = window.open("",n, "height=500,width=500");
}
function foo(obj){
test1 = "http://localhost:3001";
popUpWindow.document.write('<iframe height="450" id="receiver" allowTransparency="true" frameborder="0" scrolling="yes" style="width:100%;" src="'+test1+'" type= "text/javascript"></iframe>');
}
window.onload = function() {
var messageEle = document.getElementById('message');
console.log('its receiving message');
function receiveMessage(e) {
alert('received');
console.log('the origin is ', e.origin);
if (e.origin !== "http://localhost:3001"){
return;
}
messageEle.innerHTML = "Message Received: " + e.data;
}
window.addEventListener('message', receiveMessage);
}
</script>
OnSale
<div id="message"></div>
</body>
</html>
When i click the onSale link on the parent, it opened my child from the server 3001. However, running on the child console
parent.postMessage('Hello', 'http://localhost:3000');
does not fire the receiveMessage event of the parent. i confirmed my event is attached. I tried everything possible yet nothing change. Please what am i doing wrong ? any help would be appreciated
Sorry , after numerous attempt,
parent.opener.postMessage('Hello', 'http://localhost:3000');
Seems to work fine. Need to study the reason though . Found the answer here

IE losing iframe contents after back/forward key

This problem is only happening in IE (at least 8 and 9). After an element is dynamically added to the DOM, the contents of an embedded iframe are lost when the page is reentered with a BACK/FORWARD key. Just two small HTML files will reproduce the issue.
The first file is iframe.htm:
<!DOCTYPE html>
<html>
<head>
<title>IE iframe bug</title>
<script type="text/javascript">
function mytrace(msg) {
var t = document.createTextNode(msg);
var b = document.createElement('br');
var d = document.getElementById("trace_output")
d.appendChild(t);
d.appendChild(b); /// will work if commented
}
function submitListing() {
mytrace('submitListing()');
var doc = document.getElementById("output_iframe")
.contentWindow.document;
var d = new Date;
doc.location.replace('report.htm?invalidateCache=' + d.getTime());
//mytrace('submitListing(): out');
}
</script>
</head>
<body>
<div id="trace_output"><br /></div>
<input type="button" onclick="submitListing();" value="Run" /><br />
<iframe id="output_iframe" src=""></iframe>
</body>
</html>
The second file is report.htm:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
LINK
</body>
</html>
Steps to recreate the issue (BACK KEY)
Place above content in two files
Browse the iframe.htm file
Press the Run button to load report.htm in the iframe
Click on the LINK link to load a different page
Press the browser BACK button to returned to the "cached" (lmao) page
iframe contents are gone!!!! (only in IE-- safari, chrome, firefox retain the contents)
Also..(FORWARD KEY)
Browse to an arbitrary page (for history, http://www.google.com works)
Load iframe.htm into the same tab
Press the Run button to load report.htm in the iframe
Press the browser BACK button to return to the first page
Press the browser FORWARD button to return to iframe.htm
iframe contents are gone again!!
Now comment out the line:
d.appendChild(b)
That one change allows everything to work in IE. However, my solution needs to make those kinds of DOM manipulations (heavy jQuery/AJAX app) AND be able to restore the iframe across browser BACK/FORWARD actions.
It seems that I will have to remember the contents of the iframe so that I can restore it when the page is accessed with the BACK/FORWARD keys. I'm not thrilled with this because sometimes the iframe content will be quite large and it could chew up a bit of memory and time to make another copy of the embedded document for the restore. I would love to hear some other ideas about how I could approach this. Thanks in advance.
EDIT
The following replacement to iframe.htm will work around the problem with IE. I'm going to rewrite this using jQuery and add some more logic to restore the scroll positions. I had hoped for something more elegant, but this is doing the job.
<!DOCTYPE html>
<html>
<head>
<title>IE iframe bug</title>
<script type="text/javascript">
function myTrace(msg) {
var t = document.createTextNode(msg);
var b = document.createElement('br');
var d = document.getElementById("trace_output")
d.appendChild(t);
d.appendChild(b);
}
var make_backup ="false";
function submitListing() {
make_backup = "true";
myTrace('submitListing()');
var doc = document.getElementById("output_iframe").contentWindow.document;
var d = new Date;
doc.location.replace('report.htm?invalidateCache=' + d.getTime());
//myTrace('submitListing(): out');
}
function iframe_load() {
myTrace("iframe loaded, is_cached=" + document.getElementById("is_cached").value);
if (make_backup == "true") { // only when submitting
var htm, doc;
make_backup = "false"
doc = document.getElementById("output_iframe").contentWindow.document;
htm = doc.documentElement.innerHTML;
document.getElementById("iframe_backup").value = htmlEscape(htm);
}
}
function bodyLoaded() {
var is_cached = document.getElementById("is_cached");
if (is_cached.value == "false") { // initial page load
is_cached.value = "true";
}
else { // BACK or FORWARD, restore DOM where needed
var htm;
htm = htmlUnescape(document.getElementById("iframe_backup").value);
var doc;
doc = document.getElementById("output_iframe").contentWindow.document;
doc.open();
doc.writeln(htm);
doc.close();
}
}
function htmlEscape(str) {
return String(str).replace(/&/g, '&').replace(/"/g, '"')
.replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>');
}
function htmlUnescape(str) {
return String(str).replace(/&/g,'&').replace(/"/g,'"')
.replace(/'/g,"'").replace(/</g,'<').replace(/>/g,'>');
}
</script>
</head>
<body onload="bodyLoaded();">
<div id="trace_output" style="height: 300px; border-width:1; background-color: Silver"><br></div>
<input id="is_cached" type="hidden" value="false">
<input id="iframe_backup" type="hidden">
<input type="button" onclick="submitListing();" value="Run"><br>
<iframe id="output_iframe" src="" onload="iframe_load();"></iframe>
</body>
</html>
EDIT 2
Rewritten with jQuery:
<!DOCTYPE html>
<html>
<head>
<title>IE iframe workaround2</title>
<script type="text/javascript" src="Scripts/jquery-1.7.1.js"></script>
<script type="text/javascript">
var make_backup = "false";
$(document).ready(function () {
myTrace('document ready()');
var is_cached = $("#is_cached");
if (is_cached.val() == "false") { // initial page load
is_cached.val("true");
}
else { // BACK or FORWARD, restore DOM where needed
if ($.browser.msie) { // IE loses iframe content; restore
var htm = htmlUnescape($("#iframe_backup").val());
var doc = $("#output_iframe")[0].contentWindow.document;
doc.open();
doc.writeln(htm);
doc.close();
myTrace('iframe contents restored');
}
}
$('#output_iframe').load(function () {
myTrace("iframe_loaded");
if (make_backup == "true") { // only when submitting
make_backup = "false"
if ($.browser.msie) {
var doc = $("#output_iframe")[0].contentWindow.document;
var htm = doc.documentElement.innerHTML;
$("#iframe_backup").val(htmlEscape(htm));
myTrace('iframe contents backed up');
}
}
});
$('#submit_listing').click(function () {
make_backup = "true";
myTrace('submitListing()');
var doc = $("#output_iframe")[0].contentWindow.document;
var d = new Date;
doc.location.replace('report.htm?invalidateCache='+d.getTime());
});
});
function myTrace(msg) {
$('#trace_output').append(msg + '<br>');
}
function htmlEscape(str) {
return String(str).replace(/&/g, '&').replace(/"/g, '"')
.replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>');
}
function htmlUnescape(str) {
return String(str).replace(/&/g,'&').replace(/"/g,'"')
.replace(/'/g,"'").replace(/</g,'<').replace(/>/g,'>');
}
</script>
</head>
<body>
<div id="trace_output"
style="height: 300px; border-width:1; background-color: Silver">
<br></div>
<div style="display: block;">
<input id="is_cached" type="text" value="false">
<input id="iframe_backup" type="text" type="hidden"></div>
<input id="submit_listing" type="button" value="Run"><br>
<iframe id="output_iframe" src=""></iframe>
</body>
</html>

Dynamically uploading a file in background with JavaScript

I'm trying to upload files in the background. I am not able to use any frameworks so I have to do manually. The page already contains a form, and the file input fields are located within that form, so I can't embed a form in a form so I need to move the file input around.
The problem with the code I'm using is that it doesn't seem to actually submit, I don't see any network activity at all. Can anyone spot anything wrong here?
<form>
...
<input id="photo-file-input" type="file"/>
<button type="button" onClick="uploadBackground('photo-file-input');">Upload</button>
....
</form>
function uploadBackground(fileInputId)
{
var iframe = createIframe('TEST');
var form = createUploadForm('TEST', 'upload.php');
var fileInput = document.getElementById(fileInputId);
var fileInputParent = fileInput.parent;
//move file input into generated form
form.appendChild(fileInput);
form.submit();
iframe.onload = function()
{
alert('file was uploaded');
//put the file input back where it was
fileInputParent.appendChild(fileInput);
//clean up generated elements
iframe.parent.removeChild(iframe);
form.parent.removeChild(form);
}
}
function createUploadForm(target, action)
{
var form = document.createElement('form');
form.display = 'none';
form.target = target;
form.action = action;
form.method = 'POST';
form.enctype = 'multipart/form-data';
return form;
}
function createIframe(name)
{
var iframe;
try
{
iframe = document.createElement('<iframe name="' + name + '">');
}
catch (ex)
{
iframe = document.createElement('iframe');
iframe.name = name;
}
return iframe;
}
You can not copy a file input element and set/keep its value. It is for security reasons. There is no reason why you need to create a new form. Just append an iframe to the page, set the target of form to the iframe name and submit the original form.
I have looking for the same problem and I think, I have found an solution, which will work for Firefox and Chrome and may be on IE 10 and above ( here I need some morr testing )
The solution is a little bit ugly because I use a frameset. But this is the only solution I have found so far.
The use case is:
We have a website with an product catalog, the editor can upload videos for each product.
The upload of the video need a long time, so I have look for an solution, where after you have chosen a video an start the upload, you can navigate to an other product and upload an other file without to wait until the download of the first is complete.
The test is based on some other work:
https://stackoverflow.com/a/1186309/2248340
https://stackoverflow.com/a/105074/2248340
How it works:
If you press submit, all you form data will be placed in an object. in this object is also the selected file list.
This object will push in an array requests in the upload frame.
here runs the watchdog and look if there are new requests ( status = 0 )
If it found one a new upload is started.
Here is my test project to try it:
The frameset:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<frameset rows="*,100">
<frame id="start" name="start" src="start.html">
<frame id="upload" name="upload" src="frame.html">
</frameset>
<noframes>
<body>
please use this
</body>
</noframes>
</html>
start.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Start</title>
<script src="../js/jquery-1.11.3.min.js" ></script>
<script>
var files;
$.fn.serializeObject = function()
{
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
var UploadRequest=function(f)
{
var o= {};
o['guid']=guid();
o['action']=$('form').attr('action');
o['files']=files;
o['values']=$('form').serializeObject();
o['status']=0;
return o;
}
function fileSelect( e){
files=e.target.files;
return false;
}
</script>
</head>
<body>
<form id="test" action="phpinfo.php">
<input name="test" >
<input type="hidden" name="h1" value="2">
<input type="file" name="uploadfile" onchange="fileSelect(event)">
<input type="submit" value="upload‚" >
</form>
<script>
var olddogcounter=localStorage['uploadwatchdog'];
var check=false;
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
$(function() {
$('#test').submit(function() {
var request=new UploadRequest();
parent.upload.requests.push(request);
return false;
});
});
</script>
<a href="test.html" >test</a>
</body>
</html>
and the upload frame:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>frame</title>
</head>
<body>
<h1>frame</h1>
<iframe id="response" width="100%" height="200"></iframe>
<script>
var requests=new Array();
var counter=0;
function watchdog()
{
for(var i=0; i<requests.length; i++)
{
var request=requests[i];
if(request.status==0)
{
alert("watchdog :"+dump(request));
request.status=1;
uploadFile(request);
}
}
}
function uploadFile(request)
{
var url = request.action;
var xhr = new XMLHttpRequest();
var fd = new FormData();
xhr.open("POST", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
iframe=document.getElementById("response");
iframe.src="data:text/html;charset=utf-8," + escape(xhr.responseText);
}
};
if(request.files.length>1)
{
for(var i=0; i<request.files.length;i++)
{
var file=request.files[i];
fd.append("upload_file[]", file);
}
}
else
{
var file=request.files[0];
fd.append("upload_file",file );
}
for( var key in request.values)
{
fd.append(key,request.values[key] );
}
xhr.send(fd);
}
window.setInterval(watchdog,2000);
</script>
</body>
</html>
The solution is not complete, but I think is an good starting point.
ToDo:
- Show name of uploads in a list
- after upload remove request from array requests
- show progess bar for upload
- some error handling

call function on iframe mouse move

I have a function that i need to call on iframe mousemove(). But i didnt found anything like we have in body tag
We have <body mousemove="Function()"> Do we have anything like this for iframe??
The iframe contains its own document, own body element etc.
Try something like this:
var frame = document.getElementById("yourIframeId");
// IE is special
var frameDoc = frame.contentDocument || frame.contentWindow.document;
var frameBody = frameDoc.getElementsByTagName("body")[0];
var testingOneTwo = function() {
console.log("Hello, is this thing on?");
};
frameBody.onmouseover = testingOneTwo;
Did you mean onMouseOver or onFocus?
e.g.
<HTML>
<HEAD>
<TITLE> New Document </TITLE>
<script language="javascript">
<!--
function SayHello()
{
alert("Hi from IFrame");
}
//-->
</script>
</HEAD>
<BODY>
<iframe id="myiFrame" onMouseOver="SayHello()"/>
<iframe id="myiFrame" onFocus="SayHello()"/>
</BODY>
</HTML>

Categories