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>
Related
I know this has been asked a lot on here, but all the answers work only with jQuery and I need a solution without it.
So after I do something, my Servlet leads me to a JSP page. My JS function should populate a drop down list when the page is loaded. It only works properly when the page is refreshed tho.
As I understand this is happening because I want to populate, using innerHTML and the JS function gets called faster then my HTML page.
I also get this error in my Browser:
Uncaught TypeError: Cannot read property 'innerHTML' of null
at XMLHttpRequest.xmlHttpRequest.onreadystatechange
I had a soulution for debugging but I can't leave it in there. What I did was, every time I opened that page I automatically refreshed the whole page. But my browser asked me every time if I wanted to do this. So that is not a solution that's pretty to say the least.
Is there something I could do to prevent this?
Edit:
document.addEventListener("DOMContentLoaded", pupulateDropDown);
function pupulateDropDown() {
var servletURL = "./KategorienHolen"
let xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.onreadystatechange = function () {
if (xmlHttpRequest.readyState === 4 && xmlHttpRequest.status === 200) {
console.log(xmlHttpRequest.responseText);
let katGetter = JSON.parse(xmlHttpRequest.responseText);
JSON.stringify(katGetter);
var i;
for(i = 0; i <= katGetter.length -1; i++){
console.log(katGetter[i].id);
console.log(katGetter[i].kategorie);
console.log(katGetter[i].oberkategorie);
if (katGetter[i].oberkategorie === "B") {
document.getElementById("BKat").innerHTML += "" + katGetter[i].kategorie + "</br>";
} else if (katGetter[i].oberkategorie === "S") {
document.getElementById("SKat").innerHTML += "" + katGetter[i].kategorie + "</br>";
} else if (katGetter[i].oberkategorie ==="A") {
document.getElementById("ACat").innerHTML += "" + katGetter[i].kategorie + "</br>";
}
// document.getElementsByClassName("innerDiv").innerHTML = "" + katGetter.kategorie + "";
// document.getElementById("test123").innerHTML = "" + katGetter.kategorie + "";
}
}
};
xmlHttpRequest.open("GET", servletURL, true);
xmlHttpRequest.send();
}
It can depend on how + when you're executing the code.
<html>
<head>
<title>In Head Not Working</title>
<!-- WILL NOT WORK -->
<!--<script>
const p = document.querySelector('p');
p.innerHTML = 'Replaced!';
</script>-->
</head>
<body>
<p>Replace This</p>
<!-- Will work because the page has finished loading and this is the last thing to load on the page so it can find other elements -->
<script>
const p = document.querySelector('p');
p.innerHTML = 'Replaced!';
</script>
</body>
</html>
Additionally you could add an Event handler so when the window is fully loaded, you can then find the DOM element.
<html>
<head>
<title>In Head Working</title>
<script>
window.addEventListener('load', function () {
const p = document.querySelector('p');
p.innerHTML = 'Replaced!';
});
</script>
</head>
<body>
<p>Replace This</p>
</body>
</html>
Define your function and add an onload event to body:
<body onload="pupulateDropDown()">
<!-- ... -->
</body>
Script needs to be loaded again, I tried many options but <iframe/> works better in my case. You may try to npm import for library related to your script or you can use the following code.
<iframe
srcDoc={`
<!doctype html>
<html>
<head>
<style>[Style (If you want to)]</style>
</head>
<body>
<div>
[Your data]
<script type="text/javascript" src="[Script source]"></script>
</div>
</body>
</html>
`}
/>
Inside srcDoc, it's similar to normal HTML code.
You can load data by using ${[Your Data]} inside srcDoc.
It should work :
document.addEventListener("DOMContentLoaded", function(){
//....
});
You should be using the DOMContentLoaded event to run your code only when the document has been completely loaded and all elements have been parsed.
window.addEventListener("DOMContentLoaded", function(){
//your code here
});
Alternatively, place your script tag right before the ending body tag.
<body>
<!--body content...-->
<script>
//your code here
</script>
</body>
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>
I am trying to create an extension for Safari that will add an element to the page/tab that I am currently on. In the example below I'd like to add the Send button to the page.
The issue for me is that the new element (button) will be added to the extensions html.
Html:
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
function sendMessage() {
document.getElementById("textField").innerHTML="Sending message...";
safari.application.activeBrowserWindow.activeTab.page.dispatchMessage("hey", "there");
}
function respondToMessage(messageEvent) {
if(messageEvent.name === "gotIt")
document.getElementById("textField").innerHTML=messageEvent.message;
}
safari.self.browserWindow.addEventListener("message",respondToMessage,false);
</script>
</head>
<body> Message Sender Bar
<input type="button" value="Send" onclick="sendMessage()" >
<span id="textField">...waiting... </span>
</body>
</html>
Js:
var theBody = document.body;
var element = document.createElement("p");
element.id = "status";
element.style.cssText = "float:right; color:red";
element.textContent = "Waiting...";
theBody.insertBefore(element, theBody.firstChild);
function replyToMessage(aMessageEvent) {
if (aMessageEvent.name === "hey") {
document.getElementById("status").textContent="Message received.";
safari.self.tab.dispatchMessage("gotIt","Message acknowledged.");
}
}
safari.self.addEventListener("message", replyToMessage, false);
How can I write new lines on a frame in the same window? For now I have it creating a new Window with window.open. But now I need it in a frame.
I have an html with the two frames(form and result) and I need to write in the second by pressing a button on the first frame.
HTML
<html>
<head>
<title> Create Curriculum</title>
<meta charset="UTF-8"/>
</head>
<frameset rows="50%,50%">
<frame id="form" src="from.html"/>
<frame id="curri" src=""/>
</frameset>
You intercat with the form adding info to the inputs and saving the information. There's also a button that generates the curriculum using the info entered below.
JAVASCRIPT:
function genCurriculum() {
//I want to capture the second frame with id "curri"
var curri = parent.document.getElementById("curri");
curri.document.writeln("<h2>Head of the curriculum...</h2>");
//The others writeln for creating the curriculum
}
I want to know how to write in the frame, not in a new window.
And NOT using Jquery, just pure JavaScript. (Teacher requirement)
Is this jsfiddle what you want to have happen?
Html:
<form>
<input name="nextLine" type="text" />
<button type="button">
<span>Add a line</span>
</button>
</form>
<iframe height="100" width="200"></iframe>
JavaScript:
(function () {
'use strict';
var button, iframe, input;
function addLine (cD, input, val) {
return function () {
var body, line;
body = cD.getElementsByTagName('body')[0]
line = cD.createElement('p');
line.innerText = val.toString() + ': ' + input.value;
body.appendChild(line);
val += 1;
}
}
button = document.getElementsByTagName('button')[0];
iframe = document.getElementsByTagName('iframe')[0];
input = document.getElementsByTagName('input')[0];
button.addEventListener('click', addLine(iframe.contentDocument, input, 0), true);
}());
I need to retrieve URL parameters (which I can do successfully) and based on one parameter, decide which iframe src to fill, then with other parameters auto fill the form that is created via the form src. First issue is that I can't keep the page from going into an infinite loop. It loads properly and shows the right iframe, but the infinite loop (load) needs to stop. Second, I can't get the other parameters to autofill the input values.
Here is the code. I hope you can help. Here is the code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC- xhtml1-200000126/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!-- Keep your jQuery up to date -->
<script type="text/javascript">
var urlParams;
(window.onpopstate = function () {
var match,
pl = /\+/g, // Regex for replacing addition symbol with a space
search = /([^&=]+)=?([^&]*)/g,
decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
query = window.location.search.substring(1);
urlParams = {};
while (match = search.exec(query))
urlParams[decode(match[1])] = decode(match[2]);
})();
var test = urlParams["entry"];
var test2 = urlParams["test"];
function iframedirect(){
if (test=="sldk") {
document.getElementById("frame1").src = "http://na-sj09.marketo.com/lp/cochlearsandbox/UpgradeInterest_IFrameLandingPage.html";
var f1 = frames['frame1'].document.forms['mktForm_1108'];
f1.elements['FirstName'].value = test;
}else{
document.getElementById("frame1").src = "http://na-sj09.marketo.com/lp/cochlearsandbox/CAM-UpgradeInterestForm_iFrameLandingPage2ndOption.html";
}
}
</script>
</head>
<body id="bodyId" class="mktEditable" align="center" >
<iframe id="frame1" src="" onload="iframedirect()" height="750px" width="620px" scrolling="no" frameborder="0" marginwidth="0"></iframe>
</body>
</html>
The infinite loop is probably caused by the result of the function iframedirect().It changes the src of the iframe and triggers the onload event again and again.
You could use a variable to point out if the iframe has been loaded by iframedirect().
var test = urlParams["entry"];
var test2 = urlParams["test"];
var isLoadedByIFrameDirect = false;
function iframedirect() {
if(!isLoadedByIFrameDirect) {
if (test=="sldk") {
document.getElementById("frame1").src = "url1";
var f1 = frames['frame1'].document.forms['mktForm_1108'];
f1.elements['FirstName'].value = test;
}else{
document.getElementById("frame1").src = "url2";
}
isLoadedByIFrameDirect = true;
}
}
Okay, so the problem is that I created a sel-freferencing onload event. Bad idea. To solve the issue, I needed to remove the onload from the iframe element. I tried putting it in the Body before without luck. But I might have screwed it up, so don't ignore that option if you have a similar situation. I decided to do it with Javascript right after the function. If you are a novice, the difference between Javascript onload and HTML onload can be found here
W3Schools onload Event
I still have not solved the "autofilling iframe form from url parameter" portion of this problem. I will make an additional comment to this answer once I figure that out.
In any case, here is the code
function iframedirect() {
if(!isLoadedByIFrameDirect) {
if (test=="sldk") {
document.getElementById("frame1").src = "http://na- sj09.marketo.com/lp/cochlearsandbox/UpgradeInterest_IFrameLandingPage.html";
var f1 = frames['frame1'].document.forms['mktForm_1108'];
f1.elements['FirstName'].value = test;
}else{
document.getElementById("frame1").src = "http://na-sj09.marketo.com/lp/cochlearsandbox/CAM-UpgradeInterestForm_iFrameLandingPage2ndOption.html";
}
isLoadedByIFrameDirect = true;
}
}
window.onload = iframedirect;