In an iframe when loading content dynamically, the sequence document.open, document.write, and document.close fails in IE 9, 10, and 11 but succeeds in other browsers. However, if I assign document to a temporary variable and then do open, write, close, IE succeeds. Why is this?
(Yes, I know that there are better options besides document.open/write/close, like jquery and DOM construction, but I am curious why this fails.)
The following files show the problem:
index.htm
<!DOCTYPE html>
<html>
<head>
<title>Top Window</title>
</head>
<body>
<p>Top Window</p>
<div id="testDiv" style="width: 100%;">
<iframe src="frame.htm" style="width: 100%;"></iframe>
</div>
</body>
</html>
frame.htm
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
<p>Waiting...</p>
</body>
</html>
script.js
'use strict';
var getPage = function() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if( (this.readyState == 4) && (this.status == 200) ) {
writePage( this, "" + this.responseText );
}
};
xmlhttp.open("GET", "content.htm", true);
xmlhttp.send();
};
var writePage = function( xmlhttp, pageContent ) {
var tempDocument = document;
document.open( "text/html" );
document.write( pageContent );
document.close();
};
getPage();
content.htm
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>This is my written content.</h1>
</body>
</html>
In the writePage function, this sequence does not work (the document.open call does not return):
document.open( "text/html" );
document.write( pageContent );
document.close();
However, using the temporary reference like this:
tempDocument.open( "text/html" );
tempDocument.write( pageContent );
tempDocument.close();
works fine.
Related
I tried to use $("html").html(this.responseText);. Which replaces the content but it does not replace the head and body tags.
So for example if i want replace this content:
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<script>...</script>
<script>...</script>
</head>
<body>
<h1>This is a Heading</h1>
<script>...</script>
</body>
</html>
Then i check my HTML structure in inspector, and the result this:
<!DOCTYPE html>
<html>
<title>Page Title</title>
<script>...</script>
<script>...</script>
<h1>This is a Heading</h1>
<script>...</script>
</html>
And it messed my css so. I have tried without scripts, and it worked fine. What is the solution for this problem?
I have also tried with javascript approach
document.open();
document.write(this.responseText);
document.close();
But it confuses my javascripts. I am getting redeclaration syntax error.
My real code where i want to implement:
<script>
var frm = $('#frm');
var submitActors = frm.find('input[type=submit]');
var submitActor = null;
submitActors.click(function(event) {
submitActor = this;
});
frm.unbind('submit').submit(function () {
var formAction = document.getElementById("frm").getAttribute('action'); // Get the form action.
var data = "";
var pageUrl = "";
var test_uuid = "";
var test_number = "";
var qid = JSON.parse(sessionStorage.getItem('userChoice')).qid;
if(submitActor.name == "cmdSave"){
data = {
"cmdSave" : $("#cmdSave").val(),
"Answer": document.querySelector('input[name="Answer"]:checked').value,
"csrfmiddlewaretoken": document.querySelector('input[name=csrfmiddlewaretoken').value,
"qid": qid
}
}
else if(submitActor.name == "cmdNext"){
data = {
"cmdNext": document.querySelector('#cmdNext').value,
"csrfmiddlewaretoken":document.querySelector('input[name=csrfmiddlewaretoken').value,
"qid": qid
}
}
var httpRequest = new XMLHttpRequest();
var formData = new FormData();
Object.keys(data).forEach(function(key) {
console.log(key, data[key]);
formData.append(key, data[key]);
});
console.log(formData)
httpRequest.onreadystatechange = function(){
if ( this.readyState == 4 && this.status == 200 ) {
var response = this.responseText;
console.log(this.responseText) // Display the result inside result element.
// 1.Option
{% comment %} document.open();
document.write(this.responseText);
document.close(); {% endcomment %}
// 2.Option
{% comment %} document.documentElement.innerHTML = this.responseText; {% endcomment %}
// 3.Option
$(document).ready(function(){
$("html").html(response);
});
test_number = document.getElementById("lblNrCrt").textContent;
test_uuid = "{{test.uuid}}";
pageUrl = "/intro/" + test_uuid + "/" + test_number + "/";
window.history.pushState('', '', pageUrl);
}
};
httpRequest.open("post", formAction);
httpRequest.send(formData);
return false;
});
</script>
As I pointed out it can be done
document.querySelector('html').innerText = 'yolo';
But if you need to render HTML you should do
document.querySelector('html').innerHTML = '<div>yolo</div>';
Using Chrome browser I found that the response (or any innerHTML you want to use as the new page) has to start strictly with the <head> tag. Not <html> or <!DOCTYPE html> or anything else, otherwise replacing innerHTML or using document.write(response) always outputs the whole content inside <body>...</body> tag.
Here is the snippet/page I used locally to try all suggestions I saw (and didn't work), until I tried condensing the new page content/string to "<head>...</head><body>...</body>".
document.querySelector("#replace-content-btn").addEventListener('click', function() {
document.querySelector("html").innerHTML = `
<head>
<title>New Page Title</title>
</head>
<body>
<p>New body content.<br>Inspect the head element!</p>
</body>`;
});
<head>
<title>Some Page Title</title>
</head>
<body>
<p>Some body content.<br>Inspect the head element before and after button click.</p>
<button type="button" id="replace-content-btn">Replace entire HTML</button>
</body>
JS:
With innerHTML you can replaces all html content of element.
document.querySelector('html').innerHTML = '...'
Jquery:
$( document ).ready(function() {
$('html').html('...');
});
find tag content in your string :
function getByTag(tagname,htmlVal) {
var tagHtmValue = $(htmlVal).find(tagname).html();
return tagHtmValue;
}
replace head and and body content with the body and head in your response:
$("head").html(getByTag("head",this.responseTex));
$("body").html(getByTag("body",this.responseTex));
I have tested below code works !...
let responseTxt="<html> \
<head><title>changed hello</title></head><body><h1>Changed body</h1></body></html>"
$(document).ready(()=>{
$(".action").click(()=>{
$("html").html(responseTxt)
});
});
<html>
<head><title>hello</title></head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<h1>hello</h1>
<button class="action">Change HTML</button>
</body>
</html>
'use strict';
function phone() {
if( navigator.userAgent.match(/Android/gi)
|| navigator.userAgent.match(/webOS/gi)
|| navigator.userAgent.match(/iPhone/gi)
|| navigator.userAgent.match(/iPad/gi)
|| navigator.userAgent.match(/iPod/gi)
|| navigator.userAgent.match(/BlackBerry/gi)
|| navigator.userAgent.match(/Windows Phone/gi)
){
return true;
}
else {
return false;
}
}
<!doctype html>
<html>
<head>
<link rel=stylesheet id="style" href='style.css'/>
</head>
<body>
<script src="phone.js"></script>
<script>
phone();
</script>
</body>
</html>
The external function won't run when the page opens why is that? I even tried doing onload but nothing happened. When the page opens it should print in the console either true or false but absolutely nothing is printed.
You are not actually printing the result. You need to change to this:
<script>
console.log( "Is phone: " + phone() );
</script>
I'm trying to embedd two html documents in another using javascript. The problem I am having is that the onload event does not fire on the embedded document.
paper.htm has several div tags and should contain curve.htm but in curve.htm the onload event is not firing
paper.htm
<html>
<head>
<title>Curve</title>
<script>
(function () {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'curve.htm', true);
xhr.onreadystatechange = function () {
if (this.readyState !== 4) return;
if (this.status !== 200) return;
document.getElementById('chartBlock').innerHTML = this.responseText;
}
xhr.send();
})();
</script>
</head>
<body>
<div id="paper">
<div id="border">
<div id="chartBlock"></div>
<div id="titleBlock"></div>
</div>
</div>
</body>
</html>
curve.htm (partial)
<html>
<head>
<script src="File:///Temp/curveData.js" type="text/javascript"></script>
<script>
window.onload = init; //this never fires!
function init() { //... }
</script>
<body>
<canvas id="chartCanvas" width="954" height="625"></canvas>
</body>
</html>
I have tried:
Using jquery to load the html document (many examples here on SO).
Putting onload in the body tag of the html document (curve.htm)
tried with jquery:
<script>
$(function () {
$("#chartBlock").load("curve.htm");
$("#titleBlock").load("titleblock.htm");
});
</script>
tried with html:
<body onload="init()">
Any other ideas to look into would be very much appreciated. Thanks
Because the document will not be loaded...
What you are doing could be achieved with iframes. With an iframe, you are loading a full html document, which will be parsed as such. With the $.load function, you will just bluntly insert html, which will be parsed as <body>'s children. Meaning; the <head>, <meta> tags etc. will be ignored by all/proper browsers, because they should only occur inside the head of a document. Simplified, your output would be:
<html>
<head>
<!-- whatever -->
</head>
<body>
<div id="chartBlock">
<html>
<!-- What?! Another html tag?! -->
</html>
</div>
<div id="titleBlock">
<html>
<!-- And another one... -->
</html>
</div>
</body>
</html>
So, what you want to do, is loading only the contents, what would be inside the <body> tag, and use the success callback of the $.load function to do whatever you want to do if the contents are inserted in your document.
You could still load the page without using iframes, you just need to go about it a little differently. Basically since you're putting an html page inside of a div, the scripts on that page never get rendered.
To make it work, the scripts need to be inserted into the parent documents head.
Get scripts and insert them into head:
var head = document.getElementsByTagName('head')[0],
script = document.createElement('script'),
data;
script.type = 'text/javascript';
...
data = document.getElementById('chartBlock')
.getElementsByTagName('script').item(0).innerHTML;
try {
script.appendChild(document.createTextNode(data));
} catch(e) {
script.text = data;
}
head.insertBefore(script, head.firstChild);
head.removeChild(script);
Retrigger Load Event
var event = new Event('load');
//place after script has been inserted into head
window.dispatchEvent(event);
Using your example above:
curve.htm
<html>
<head>
<script src="File:///Temp/curveData.js"></script>
<script>
window.onload = init; //this will now fire
function init() { alert('loaded'); }
</script>
</head>
<body>
<canvas id="chartCanvas" width="954" height="625"></canvas>
</body>
</html>
paper.htm
<html>
<head>
<title>Curve</title>
<script>
(function () {
var xhr = new XMLHttpRequest(),
event = new Event('load'),
//since the onload event will have already been triggered
//by the parent page we need to recreate it.
//using a custom event would be best.
head = document.getElementsByTagName('head')[0],
script = document.createElement('script'),
data;
script.type = 'text/javascript';
xhr.open('GET', 'curve.htm', true);
xhr.onreadystatechange = function (){
if (this.readyState !== 4) return;
if (this.status !== 200) return;
document.getElementById('chartBlock').innerHTML = this.responseText;
data = document.getElementById('chartBlock')
.getElementsByTagName('script').item(0).innerHTML;
//we're extracting the script containing the
//onload handlers from curve.htm,
//note that this will only cover the first script section..
try {
// doesn't work on ie
script.appendChild(document.createTextNode(data));
} catch(e) {
script.text = data;
}
head.insertBefore(script, head.firstChild);
head.removeChild(script);
window.dispatchEvent(event);
//once the scripts have been rendered,
//we can trigger the onload event
}
xhr.send();
})();
</script>
</head>
<body>
<div id="paper">
<div id="border">
<div id="chartBlock"></div>
<div id="titleBlock"></div>
</div>
</div>
</body>
</html>
I rarely have to do any Javascript and I seem to fail doing the easiest tasks. I am trying to replace a string in two divs. The first div gets replaced, the second one is not found with the error message:
drawings.html:20 Uncaught TypeError: Cannot set property 'innerHTML' of null
However I tried the usual remedies of putting my code in an 'onload' function and putting the script at the end of the body tag. What else could possibly go wrong?
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="cell1">test<div>
<div id="cell2">test<div>
<script>
window.onload = function() {
replace();
}
function replace() {
console.log("replace");
document.getElementById("cell1").innerHTML = "cell1";
document.getElementById("cell2").innerHTML = "cell2";
}
</script>
</body>
</html>
just close your divs elements.
<html>
<head>
</head>
<body>
<div id="cell1">test</div>
<div id="cell2">test</div>
<script>
window.onload = function() {
replace();
}
function replace() {
console.log("replace");
document.getElementById("cell1").innerHTML = "cell1";
document.getElementById("cell2").innerHTML = "cell2";
}
</script>
</body>
</html>
I'm developing in an application where new code is introduced via iframes only. I'm trying to create a function (running inside an iFrame) that will append some html after the iFrame in which it is running. I'd rather not introduce jQuery to resolve this issue. Here is a sample
function AppendDivAfteriFrame() {
var iFrame = window.parent.document.getElementById(window.frameElement.id)
var newDivInParent = window.parent.document.createElement('div');
newDivInParent.setAttribute("Id", "MyDiv");
newDivInParent.innerHTML = 'Hello World! I am outside the iFrame';
iFrame.appendChild(newDivInParent);
}
I don't receive any exceptions, but I never see any results either.
Update with full code
Call this page InnerPage.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
function AppendDivAfteriFrame() {
var iFrame = window.parent.document.getElementById(window.frameElement.id)
var newDivInParent = window.parent.document.createElement('div');
newDivInParent.setAttribute("Id", "MyDiv");
iFrame.appendChild(newDivInParent);
parent.document.getElementById('MyDiv').innerHTML = 'Hello World! I am outside the iFrame';
}
</script>
</head>
<body>
<button onclick="AppendDivAfteriFrame()">Append HTML</button>
</body>
</html>
Call this page OuterPage.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<iframe src="InnerPage.html" id="MyiFrame"></iframe>
</body>
</html>
This works:
function AppendDivAfteriFrame() {
var iFrame = window.parent.document.getElementById(window.frameElement.id)
var newDivInParent = window.parent.document.createElement('div');
newDivInParent.setAttribute("Id", "MyDiv");
iFrame.parentNode.appendChild(newDivInParent); //Edited here
parent.document.getElementById('MyDiv').innerHTML = 'Hello World! I am outside the iFrame';
}