How to get WHOLE content of iframe? - javascript

I need to get whole content of iframe from the same domain. Whole content means that I want everything starting from <html> (including), not only <body> content.
Content is modified after load, so I can't get it once again from server.

I belive I've found the best solution:
var document = iframeObject.contentDocument;
var serializer = new XMLSerializer();
var content = serializer.serializeToString(document);
In content we have full iframe content, including DOCTYPE element, which was missing in previous solutions. And in addition this code is very short and clean.

If it is on the same domain, you can just use
iframe.contentWindow.document.documentElement.innerHTML
to get the content of the iframe, except for the <html> and </html> tag, where
iframe = document.getElementById('iframeid');

$('input.test').click(function(){
$('textarea.test').text($('iframe.test').contents());
});

You can get the literal source of any file on the same domain with Ajax, which does not render the html first-
//
function fetchSource(url, callback){
try{
var O= new XMLHttpRequest;
O.open("GET", url, true);
O.onreadystatechange= function(){
if(O.readyState== 4 && O.status== 200){
callback(O.responseText);
}
};
O.send(null);
}
catch(er){}
return url;
}
function printSourceCode(text){
var el= document.createElement('textarea');
el.cols= '80';
el.rows= '20';
el.value= text;
document.body.appendChild(el);
el.focus();
}
fetchSource(location.href, printSourceCode);

Related

Load XML, apply XSL and append to existing HTML page

On an already loaded HTML page I want to load a XML file and append the resultig HTML to the loaded page. The XML file is linked with an XSL file. When I open it in the browser (Firefox) the XSL transformation is correctly applied. The result is HTML without html, head and body tags. That’s what I want.
Loading the XML with Ajax gives me the untransformed XML, not the HTML I wish.
My workaround so far is to load the XML file in a hidden iframe. In there the XSL transformation is correctly carried out.
I now fail to copy the HTML form the iframe and append it to an existing div in the page outside the iframe. I only manage to:
Get an HTML Collection (with jquery or plain JS):
iframe = $('#iframe');
iframeHtml = iframe.contents()[0].children;
iframe = document.getElementById('bausteine__liste__zwischenspeicher');
resultHtml = iframe.contentDocument || temp.contentWindow.document;
Get all HTML but only starting below the first div (with jquery). The uppermost div which contains everything is left out.
iframe = $('#iframe');
resultHtml = iframe.contents().find('div').html();
This is the iframe as I see it in the dev toolbar of Firefox
<iframe id="iframe" src="file-with-xslt.xml">
<div class="uppermost-element">
<div>
…
</div>
<div>
…
</div>
</div>
</iframe>
How can I get the HTML including the uppermost element? Either via the iframe or – even better – via Ajax.
Thanks for your help!
The solution is to load both the XML and XSL with an XMLHttpRequest and to do the transformation with Javascript. documentElement.outerHTML will output the resulting HTML.
function XMLtransformation(xsl, xml) {
var xslStylesheet;
var xsltProcessor = new XSLTProcessor();
var xmlDoc;
// load XSL file
var myXMLHTTPRequest = new XMLHttpRequest();
myXMLHTTPRequest.open('GET', urlXSL, false);
myXMLHTTPRequest.send(null);
xslStylesheet = myXMLHTTPRequest.responseXML;
xsltProcessor.importStylesheet(xslStylesheet);
// load XML file
myXMLHTTPRequest = new XMLHttpRequest();
myXMLHTTPRequest.open('GET', urlXML, false);
myXMLHTTPRequest.send(null);
xmlDoc = myXMLHTTPRequest.responseXML;
var htmlDocument = xsltProcessor.transformToDocument(xmlDoc, document);
return htmlDocument.documentElement.outerHTML;
}
XMLtransformation('file.xsl', 'file.xml');
I liked #Moritz's solution above, but it needed a bit of tweaking so here's my version:
function XMLtransformation(xslUrl, xmlUrl) {
const errorMessage = 'Unable to load the content';
const parser = new DOMParser();
// attempt to load the XSL file
const xslRequest = new XMLHttpRequest();
xslRequest.open('GET', xslUrl, false); // `false` makes the request synchronous
xslRequest.send(null);
if (xslRequest.status < 300) {
const xslStylesheet = parser.parseFromString(xslRequest.response, "application/xml");
const xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xslStylesheet);
const xmlRequest = new XMLHttpRequest();
xmlRequest.open('GET', xmlUrl, false);
xmlRequest.send(null);
if (xmlRequest.status < 300) {
const htmlDocument = xsltProcessor.transformToDocument(
parser.parseFromString(xmlRequest.response, "application/xml"),
document
);
return htmlDocument.documentElement.outerHTML;
} else {
console.error('xml load failure:');
console.error(xmlRequest.status, xmlRequest.responseText);
}
} else {
console.error('xsl load failure:');
console.error(xslRequest.status, xslRequest.responseText);
}
return errorMessage;
}

Javascript - DOM parser load ajax requests, scripts no run

When a user clicks on a link instead of loading a whole new page I load the new page's HTML data through an ajax request (and also with a query string I get the server to not send the nav bar data each time) the resulting data from the ajax request I then put through DOMParser to allow me to just get the content from the div with the id of "content" and replace the current document's "context" div's innerHTML.
After doing a request through this method though any script tags within the newDOM don't run after being put in the content div. Also, it does appear to run while it is in newDOM either, because if you have a script that instantly edits the document while it loads there is no effect when you log out newDOM
AjaxRequest(href, function(data) {
var parser = new DOMParser();
var newDOM = parser.parseFromString(data.responseText, "text/html");
//Setup new title
var title = '';
if (newDOM.getElementsByTagName('title').length > 0 && newDOM.getElementsByTagName('title')[0] !== null) {
title = newDOM.getElementsByTagName('title')[0].innerHTML;
} else {
title = rawhref;
}
document.title = title;
history.pushState({}, title, rawhref);
if (newDOM.getElementById('content') === null) {
//If there is an error message insert whole body into the content div to get full error message
document.getElementById('content').appendChild(newDOM.getElementsByTagName('body')[0]);
} else {
document.getElementById('content').appendChild(newDOM.getElementById('content'));
}
MapDOM();
if (typeof(onPageLoad) == "function") {
onPageLoad();
}
});
Note: the variable "rawhref" is just the request URL without ?noheader so that it will be easier for users to go back though their history.
NOTE: Also after any new load I also have a function that overwrites any new a tag so that it will work though this method for the next new page.
Also, it would be much preferred if the answer didn't use jQuery.
Some one just answered this and while I was testing it they deleted their solution.... Um, thanks so much who ever you were, and for anyone in the future who has this problem here is the code they showed, but I didn't have time to fully understand why it worked.... but I think can work it out.
function subLoader(dest, text) {
var p = new DOMParser();
var doc = p.parseFromString(text, 'text/html');
var f = document.createDocumentFragment();
while (doc.body.firstChild) {
f.appendChild(doc.body.firstChild);
}
[].map.call(f.querySelectorAll('script'), function(script) {
var scriptParent = script.parentElement || f;
var newScript = document.createElement('script');
if (script.src) {
newScript.src = script.src;
} else {
newScript.textContent = script.textContent;
}
scriptParent.replaceChild(newScript, script);
});
dest.appendChild(f);
}

HTML JavaScript delay downloading img src until node in DOM

Hi I have markup sent to me from a server and I set it as the innerHTML of a div element for the purpose of traversing the tree, finding image nodes, and changing their src values. Is there a way to prevent the original src value from being downloaded?
Here is what I am doing
function replaceImageSrcsInMarkup(markup) {
var div = document.createElement('div');
div.innerHTML = markup;
var images = div.getElementsByTagName('img');
images.forEach(replaceSrc);
return div.innerHTML;
}
The problem is that in browsers as soon as you do:
var img = document.createElement('img'); img.src = 'someurl.com' the browser fires off a request to someurl.com. Is there a way to prevent this without resorting to parsing the markup myself? If there is in no other way does anyone know a good way of parsing the markup with as little code as possible to accomplish my goal?
I know you are already happy with your solution, but I think it would be worth sharing a safe method for future users.
You can now simply use the DOMParser object to generate an external document from your HTML string, instead of using a div created by your current document as container.
DOMParser specifically avoids the pitfalls mentioned in the question and other threats: no img src download, no JavaScript execution, even in elements attributes.
So in your case you can safely do:
function replaceImageSrcsInMarkup(markup) {
var parser = new DOMParser(),
doc = parser.parseFromString(markup, "text/html");
// Manipulate `doc` as a regular document
var images = doc.getElementsByTagName('img');
for (var i = 0; i < images.length; i += 1) {
replaceSrc(images[i]);
}
return doc.body.innerHTML;
}
Demo: http://jsfiddle.net/94b7gyg9/1/
Note: with your current code, browsers will still try downloading the resource initially specified in your img nodes src attribute, even if you change it before the end of JS execution. Trace network transactions in this demo: http://jsfiddle.net/94b7gyg9/
Rather than append the new markup to the DOM before you change the img sources, create an element, set it's inner HTML, change the source of the images and then finally, append the changed markup to the page.
Here's a fully-worked sample.
<!DOCTYPE html>
<html>
<head>
<script>
"use strict";
function byId(id,parent){return (parent == undefined ? document : parent).getElementById(id);}
//function allByClass(className,parent){return (parent == undefined ? document : parent).getElementsByClassName(className);}
function allByTag(tagName,parent){return (parent == undefined ? document : parent).getElementsByTagName(tagName);}
function newEl(tag){return document.createElement(tag);}
//function newTxt(txt){return document.createTextNode(txt);}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded()
{
byId('goBtn').addEventListener('click', onGoBtnClick, false);
}
var dummyString = "<img src='img/girl.png'/><img src='img/gfx07.jpg'/>";
function onGoBtnClick(evt)
{
var div = newEl('div');
div.innerHTML = dummyString;
var mImgs = allByTag('img', div);
for (var i=0, n=mImgs.length; i<n; i++)
{
mImgs[i].src = "img/murderface.jpg";
}
document.body.appendChild(div);
}
</script>
<style>
</style>
</head>
<body>
<button id='goBtn'>GO!</button>
</body>
</html>
You could directly parse the markup string using a regex to replace the img src. Searching for all the img src urls in the string and then replacing them with the new url.
var regex = /<img[^>]+src="?([^"\s]+)"?\s*\/>/g;
var imgUrls = [];
while ( m = regex.exec( markup ) ) {
imgUrls.push( m[1] );
}
imgUrls.forEach(function(url) {
markup = markup.replace(url,'new-url');
});
Another solution might be, if you have access to it, to set the all the img src to an empty string, and put the url in in a data-src attribute. Having your markup string look like something like this
markup = '
';
Then setting this markup to your div.innerHTML won't trigger any download from the browser. And you can still parse it using regular DOM selector.
div.innerHTML = markup;
var images = div.getElementsByTagName('img');
images.forEach(function(img){
var oldSrc = img.getAttribute('data-src');
img.setAttribute('src', 'new-url');
});

Get the text from an external HTML document

My goal is to get the text from a HTML document which does not call any functions from my .jsp file.
I've looked around and I thought I had found the answer to my problem but it doesn't seem to be working, and other answers consist of using jQuery (which I am both unfamiliar with and not allowed to use).
This is my code so far:
function getText(divID) {
var w = window.open("test.html");
var body = w.document.body;
var div = document.getElementById(divID);
var textContent = body.textContent || body.innerText;
console.log(textContent);
//div.appendChild(document.createTextNode(textContent));
}
So as you can see, I'm trying to get the body of one HTML document and have it appear in another. Am I on the right tracks?
EDIT: Ok so I seem to have made my problem quite confusing. I call the function in a HTML document called html.html, but I want to get the text from test.html, then have it appear in html.html. It has to be like this because I can't assume that the HTML document I want to read from will include my .jsp file in its head.
At the moment I am getting the following error.
Uncaught TypeError: Cannot read property 'body' of undefined
The reason document.body in the other window is undefined, is because the other window has not loaded and rendered the document yet.
One solution would be to wait for the onload event.
function getText(divID) {
var w = window.open("test.html");
w.addEventListener("load", function() {
var body = w.document.body;
var div = document.getElementById(divID);
var textContent = body.textContent || body.innerText;
console.log(textContent);
});
}
Make sure you run the getText function on a user event like a click, else window.open will fail.
If all you want to do is get the contents of the other window, using AJAX would probably be a better option.
function getText(divID) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 ) {
var body = xhr.response.body;
var div = document.getElementById(divID);
var textContent = body.textContent || body.innerText;
console.log(textContent);
}
};
xhr.open("GET", "test.html", true);
xhr.responseType = "document";
xhr.send();
}

How to force a script reload and re-execute?

I have a page that is loading a script from a third party (news feed). The src url for the script is assigned dynamically on load up (per third party code).
<div id="div1287">
<!-- dynamically-generated elements will go here. -->
</div>
<script id="script0348710783" type="javascript/text">
</script>
<script type="javascript/text">
document.getElementById('script0348710783').src='http://oneBigHairyURL';
</script>
The script loaded from http://oneBigHairyURL then creates and loads elements with the various stuff from the news feed, with pretty formatting, etc. into div1287 (the Id "div1287" is passed in http://oneBigHairyURL so the script knows where to load the content).
The only problem is, it only loads it once. I'd like it to reload (and thus display new content) every n seconds.
So, I thought I'd try this:
<div id="div1287">
<!-- dynamically-generated elements will go here. -->
</div>
<script id="script0348710783" type="javascript/text">
</script>
<script type="javascript/text">
loadItUp=function() {
alert('loading...');
var divElement = document.getElementById('div1287');
var scrElement = document.getElementById('script0348710783');
divElement.innerHTML='';
scrElement.innerHTML='';
scrElement.src='';
scrElement.src='http://oneBigHairyURL';
setTimeout(loadItUp, 10000);
};
loadItUp();
</script>
I get the alert, the div clears, but no dynamically-generated HTML is reloaded to it.
Any idea what I'm doing wrong?
How about adding a new script tag to <head> with the script to (re)load? Something like below:
<script>
function load_js()
{
var head= document.getElementsByTagName('head')[0];
var script= document.createElement('script');
script.src= 'source_file.js';
head.appendChild(script);
}
load_js();
</script>
The main point is inserting a new script tag -- you can remove the old one without consequence. You may need to add a timestamp to the query string if you have caching issues.
Here's a method which is similar to Kelly's but will remove any pre-existing script with the same source, and uses jQuery.
<script>
function reload_js(src) {
$('script[src="' + src + '"]').remove();
$('<script>').attr('src', src).appendTo('head');
}
reload_js('source_file.js');
</script>
Note that the 'type' attribute is no longer needed for scripts as of HTML5. (http://www.w3.org/html/wg/drafts/html/master/scripting-1.html#the-script-element)
Creating a new script tag and copying the contents of the existing script tag, and then adding it, works well.
var scriptTag = document.createElement('script');
scriptTag.innerText = "document.body.innerHTML += 'Here again ---<BR>';";
var head = document.getElementsByTagName('head')[0];
head.appendChild(scriptTag);
setInterval(function() {
head.removeChild(scriptTag);
var newScriptTag = document.createElement('script');
newScriptTag.innerText = scriptTag.innerText;
head.appendChild(newScriptTag);
scriptTag = newScriptTag;
}, 1000);
This won't work if you expect the script to change every time, which I believe is your case. You should follow Kelly's suggestion, just remove the old script tag (just to keep the DOM slim, it won't affect the outcome) and reinsert a new script tag with the same src, plus a cachebuster.
Small tweak to Luke's answer,
function reloadJs(src) {
src = $('script[src$="' + src + '"]').attr("src");
$('script[src$="' + src + '"]').remove();
$('<script/>').attr('src', src).appendTo('head');
}
and call it like,
reloadJs("myFile.js");
This will not have any path related issues.
Use this function to find all script elements containing some word and refresh them.
function forceReloadJS(srcUrlContains) {
$.each($('script:empty[src*="' + srcUrlContains + '"]'), function(index, el) {
var oldSrc = $(el).attr('src');
var t = +new Date();
var newSrc = oldSrc + '?' + t;
console.log(oldSrc, ' to ', newSrc);
$(el).remove();
$('<script/>').attr('src', newSrc).appendTo('head');
});
}
forceReloadJS('/libs/');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
I know that is to late, but I want to share my answer.
What I did it's save de script's tags in a HTML file,
locking up the scripts on my Index file in a div with an id, something like this.
<div id="ScriptsReload"><script src="js/script.js"></script></div>
and when I wanted to refresh I just used.
$("#ScriptsReload").load("html_with_scripts_tags.html", "", function(
response,
status,
request
) {
});

Categories

Resources