Duplicate iframe: Copy head and body from 1 iframe to another - javascript

Simple question which I can't seem to find an answer of:
I have two iframes on a page and I'd like to copy the content of the first one to the second.
But I can't do it by just copying the url of the first iframe to the second since the containing page is a dynamic one.
This code does do it, but a lot of the page-formatting seems to get lost. And I don't know if it's cross-browser either.
iframe2.contentWindow.document.write(iframe1.contentWindow.document.body.innerHTML);
Can this be done?

Native JavaScript Solution As Asked For:
First, to make things simple I created 2 object literals:
var iframe1 = {
doc : undefined,
head : undefined,
body : undefined
};
var iframe2 = {
doc : undefined,
head : undefined,
body : undefined
};
Next, I put everything under iframe1's window.onload handler to make sure it was loaded fully:
document.getElementById("iframe1").contentWindow.onload = function() {
Then I assigned all of the object literal properties:
iframe1.doc = document.getElementById("iframe1").contentWindow.document;
iframe1.head = iframe1.doc.getElementsByTagName("head")[0];
iframe1.body = iframe1.doc.getElementsByTagName("body")[0];
iframe2.doc = document.getElementById("iframe2").contentWindow.document;
iframe2.head = iframe2.doc.getElementsByTagName("head")[0];
iframe2.body = iframe2.doc.getElementsByTagName("body")[0];
Next, I needed to create a couple functions removeNodes() and appendNodes() so that I could re-factor some code that is used for both <head> and <body> routines.
function removeNodes(node) {
while (node.firstChild) {
console.log("removing: " + node.firstChild.nodeName);
node.removeChild(node.firstChild);
}
}
and:
function appendNodes(iframe1Node, iframe2Node) {
var child = iframe1Node.firstChild;
while (child) {
if (child.nodeType === Node.ELEMENT_NODE) {
console.log("appending: " + child.nodeName);
if (child.nodeName === "SCRIPT") {
// We need to create the script element the old-fashioned way
// and append it to the DOM for IE to recognize it.
var script = iframe2.doc.createElement("script");
script.type = child.type;
script.src = child.src;
iframe2Node.appendChild(script);
} else {
// Otherwise, we append it the regular way. Note that we are
// using importNode() here. This is the proper way to create
// a copy of a node from an external document that can be
// inserted into the current document. For more, visit MDN:
// https://developer.mozilla.org/en/DOM/document.importNode
iframe2Node.appendChild(iframe2.doc.importNode(child, true));
}
}
child = child.nextSibling;
}
With those functions created, now all we have to do is make our calls:
console.log("begin removing <head> nodes of iframe2");
removeNodes(iframe2.head);
console.log("begin removing <body> nodes of iframe2");
removeNodes(iframe2.body);
console.log("begin appending <head> nodes of iframe1 to iframe2");
appendNodes(iframe1.head, iframe2.head);
console.log("begin appending <body> nodes of iframe1 to iframe2");
appendNodes(iframe1.body, iframe2.body);
... and finally, we close off the window.onload function:
};

Related

How to get a list of Javascript functions in a specific <script> tag

Using the HTML below, how can I get a list of the functions in the <script> tag that is IN the #yesplease div. I don't want any other functions from other script tags. I don't want any global functions or native functions. What I'd like is an array with "wantthis1" and "wantthis2" only in it. I'm hoping not to have to use regex.
I am going to be emptying and filling the #yesplease div with different strings of html (including new script tags with new functions), and I need to make sure that I delete or "wantthis1 = undefined" each function in the script tag before filling it, programmatically, since I won't know every function name. (I don't want them to remain in memory)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script>
function dontCare() {
// don't care
}
</script>
</head>
<body>
<div id="notthisone">
<p>
Hello
</p>
<script>
function dontwantthis () {
// nope
}
</script>
</div>
<div id="yesplease">
<p>
Hello again
</p>
<script>
function wantthis1 () {
// yes
}
function wantthis2 () {
// yes
}
</script>
</div>
<script>
// this function can be called by whatever, but this will look to
// see if functions exist, then call them, otherwise do something
// else
function onSaveOrWhatever () {
if (wantThis1 !== "undefined") {
wantThis1();
}
else {
// do something else (won't get called with above example)
}
if (wantThis3 !== "undefined") {
wantThis3();
}
else {
// do something else (will get called with above example)
}
}
</script>
</body>
</html>
Take innerHTML of all script tags you need
Create an iframe
Get a list of built-in functions of iframe.contentWindow object
Write the content of the script to the iframe created
Get a new list of the functions of iframe.contentWindow object
Find new functions added to the new list
Somehow it doesn't work in stack snippets but it works in Codepen link
var code = document.querySelector("#target script").innerHTML;
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
var builtInFunctions = getFunctionsOfWindowObject();
var html = `<html><head><script>${code}</script></head><body /></html>`;
iframe.srcdoc = html;
var allFunctions = getFunctionsOfWindowObject();
var result = allFunctions.filter(function(n) {
return builtInFunctions.indexOf(n) < 0;
});
console.log(result);
function getFunctionsOfWindowObject() {
var functions = [];
var targetWindow = iframe.contentWindow;
for (var key in targetWindow) {
if (
targetWindow.hasOwnProperty(key) &&
typeof targetWindow[key] === "function"
) {
functions.push(key);
}
}
return functions;
}
iframe {
display: none;
}
<div id="target">
<script>
function wantthis1() {}
function wantthis2() {}
</script>
</div>
The are a few ways to solve this problem
Architect your application. Use react or vue (or even jquery) to add more control to your code/dom
AST parsing, which would be overkill
Hack it
If you hack it, the problem that you will face is that you are adding functions to global scope. This is shared by everyone, so you can't really monitor it in a nice way.
You can however take advantage of javascript singlethreadedness, and know that things won't happen in the background while you are doing monitoring tasks.
<script>
// take a cache of what is present before your script
window.itemsPresentBeforeScript = {};
foreach (var item in window) {
window.itemsPresentBeforeScript[item] = window[item];
}
</script>
<script> .... your other script </script>
<script>
// use your first cache to see what changed during the script execution
window.whatWasAddedInTheLastScript = {};
foreach (var item in window) {
if (!window.itemsPresentBeforeScript[item]) {
window.whatWasAddedInTheLastScript[item] = window[item];
}
}
delete window.itemsPresentBeforeScript;
// not you have a global list of what was added and you can clear it down when you need to
</script>

How to select/hide elements inside an object of type "text/html" using javascript [duplicate]

I'm using the object tag to load an html snippet within an html page.
My code looks something along these lines:
<html><object data="/html_template"></object></html>
As expected after the page is loaded some elements are added between the object tags.
I want to get those elements but I can't seem to access them.
I've tried the following
$("object").html() $("object").children() $("object")[0].innerHTML
None of these seem to work. Is there another way to get those elements?
EDIT:
A more detailed example:
consider this
<html><object data="http://www.YouTube.com/v/GGT8ZCTBoBA?fs=1&hl=en_US"></object></html>
If I try to get the html within the object I get an empty string.
http://jsfiddle.net/wwrbJ/1/
As long as you place it on the same domain you can do the following:
HTML
<html>
<object id="t" data="/html_template" type="text/html">
</object>
</html>
JavaScript
var t=document.querySelector("#t");
var htmlDocument= t.contentDocument;
Since the question is slightly unclear about whether it is also about elements, not just about the whole innerHTML: you can show element values that you know or guess with:
console.log(htmlDocument.data);
The innerHTML will provide access to the html which is in between the <object> and </object>. What is asked is how to get the html that was loaded by the object and inside the window/frame that it is producing (it has nothing to do with the code between the open and close tags).
I'm also looking for an answer to this and I'm afraid there is none. If I find one, I'll come back and post it here, but I'm looking (and not alone) for a lot of time now.
No , it's not possible to get access to a cross-origin frame !
Try this:
// wait until object loads
$('object').load(function() {
// find the element needed
page = $('object').contents().find('div');
// alert to check
alert(page.html());
});
I know this is an old question, but here goes ...
I used this on a personal website and eventually implemented it in some work projects, but this is how I hook into an svg's dom. Note that you need to run this after the object tag has loaded (so you can trigger it with an onload function). It may require adaptation for non-svg elements.
function hooksvg(elementID) { //Hook in the contentDocument of the svg so we can fire its internal scripts
var svgdoc, svgwin, returnvalue = false;
var object = (typeof elementID === 'string' ? document.getElementById(elementID) : elementID);
if (object && object.contentDocument) {
svgdoc = object.contentDocument;
}
else {
if (typeof object.getSVGDocument == _f) {
try {
svgdoc = object.getSVGDocument();
} catch (exception) {
//console.log('Neither the HTMLObjectElement nor the GetSVGDocument interface are implemented');
}
}
}
if (svgdoc && svgdoc.defaultView) {
svgwin = svgdoc.defaultView;
}
else if (object.window) {
svgwin = object.window;
}
else {
if (typeof object.getWindow == _f) {
try {
svgwin = object.getWindow();//TODO look at fixing this
}
catch (exception) {
// console.log('The DocumentView interface is not supported\r\n Non-W3C methods of obtaining "window" also failed');
}
}
}
//console.log('svgdoc is ' + svgdoc + ' and svgwin is ' + svgwin);
if (typeof svgwin === _u || typeof svgwin === null) {
returnvalue = null;
} else {
returnvalue = svgwin;
}
return returnvalue;
};
If you wanted to grab the symbol elements from the dom for the svg, your onload function could look like this:
function loadedsvg(){
var svg = hooksvg('mysvgid');
var symbols = svg.document.getElementsByTagName('symbol');
}
You could use the following code to read object data once its loaded completely and is of the same domain:
HTML-
<html>
<div class="main">
<object data="/html_template">
</object>
</div>
</html>
Jquery-
$('.main object').load(function() {
var obj = $('.main object')[0].contentDocument.children;
console.log(obj);
});
Hope this helps!
Here goes a sample piece of code which works. Not sure what the problem is with your code.
<html>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
var k = $("object")[0].innerHTML;
alert(k);
$("object")[0].innerHTML = "testing";
});
</script>
<object data="/html_template">hi</object>
</html>
UPDATED
I used this line of Javascript to change the value of a input filed inside an iFrame, taken from How to pick element inside iframe using document.getElementById:
document.getElementById('iframeID').contentWindow.document.getElementById('inputID').value = 'Your Value';
In your case, since you do not have a frame, and since you want to get and not set the value, log it for example with:
console.log(document.getElementById('object').value);
And if you guess or choose an element:
console.log(document.getElementById('object').data);

VueJS: How to dynamically execute Javascript from string?

I'm writing a website using VueJS which allows (selected) users to add scripts that are automatically executed upon page load. Here's a sample text that a user might upload:
<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.0.5/howler.js"></script>
<script>
var sound = new howler.Howl({
src: ['./sample.mp3']
)}.play();
</script>
This text is stored into a string after retrieving from API backend. The problem now is: I couldn't get it to execute however I try. Is there an option in VueJS that can automatically execute javascripts in strings?
As a reference, here's my code:
var temp_arr = utils.preprocess(vm.chapterInfo.Content)
vm.display = temp_arr[0]
vm.control_script = console.log(temp_arr[1])
// None of below worked
eval(vm.control_script)
document.getElementsByTagName("head")[0].appendChild(control_script)
The problem isn't a Vue one, but a JavaScript one.
I assume that you already understand the security implications of allowing users to run JavaScript; it's rarely a good idea. Sites like JSFiddle do it successfully, however it will take a lot of work and understanding to make it safe, so if you're not 100% sure with what you are doing, then as #WaldemarIce said, you shouldn't do it!
Right, with the warning out the way, you need to do a few things to get this to work:
1) Load the external scripts:
loadScripts() {
return new Promise(resolve => {
let scriptEl = document.createElement("script");
scriptEl.src = "https://cdnjs.cloudflare.com/ajax/libs/howler/2.0.5/howler.js";
scriptEl.type = "text/javascript";
// Attach script to head
document.getElementsByTagName("head")[0].appendChild(scriptEl);
// Wait for tag to load before promise is resolved
scriptEl.addEventListener('load',() => {
resolve();
});
});
}
Here I'm simply attaching the external script to the head of the document and attaching a load event, which resolves the Promise when loaded.
2) Now we have loaded the external script we can execute the remainder of the script. You will need to strip out the script tags, so you can do something like this:
executeScript() {
// remove script tags from string (this has been declared globally)
let script = string.replace(/<\/?script>/g,"")
eval(script)
}
Form the Vue perspective, you can then execute this inside the created hook:
created() {
this.loadScripts().then(() => {
this.executeScript();
});
},
I'll leave it to you to extract the external scripts you want to load from your user input, but here's a JSFiddle: https://jsfiddle.net/49dq563d/
I recently came across this problem and had to extend on the answer from #craig_h. The example below allows full embed code to be sent through as string (HTML elements as well as scripts and inline JS). This is using DOMParser.
<div ref="htmlDump"></div>
<script>
import Vue from "vue";
export default {
...
methods: {
cloneAttributes(element, sourceNode) {
let attr;
let attributes = Array.prototype.slice.call(sourceNode.attributes);
while(attr = attributes.pop()) {
element.setAttribute(attr.nodeName, attr.nodeValue);
}
}
},
mounted(){
if(this.embedString && this.embedString.length > 0)
{
//Parse the code given from the API into a new DOM so we can easily manipulate it
var parser = new DOMParser();
var htmlDoc = parser.parseFromString(this.embedString, 'text/html');
//Get the contents of the new DOM body and loop through.
//We want to add all HTML elements to the page and run / load all JS
var kids = [...htmlDoc.body.children];
let len = kids.length;
for (var i = 0; i < len; i++) {
var item = kids[i];
if(item.tagName == "SCRIPT")
{
//If we have a 'src' attribute then we're loading in a script
if(item.hasAttribute('src'))
{
//Create a new element within the current doc to trigger the script load
let scriptEl = document.createElement("script");
//Copy all attributes from the source element to the new one
this.cloneAttributes(scriptEl, item);
//Attach script to the DOM to trigger it to load
this.$refs.htmlDump.appendChild(scriptEl);
} else {
//if we don't have a 'src' attribute then we have some code to run
eval(item.innerText);
}
} else{
this.$refs.htmlDump.appendChild(item);
}
}
}
}
...
}
</script>

Reference same element from multiple parent nodes in jquery?

It seems that jquery's append method can only move or clone an element, but what I want is to insert it into multiple parent nodes such that any updates to the element will be reflected immediately "from different places". Do I have to use raw DOM maniplulation to accomplish that, or is there some API I've overlooked?
Well, after a bit of research and testing, I've finally come to the conclusion that this just isn't (directly) possible. For one thing, the Document Object Model requires that an HTML node maintain a reference to its parent. Second, workarounds such as patching a node's innerHTML property fail because it's implemented (at least on some browsers) as an immutable "native object" of sorts.
Here's an example:
<!DOCTYPE html>
<html>
<head>
<script>
function demonstrate()
{
function InnerHTMLReference(reference)
{
this.reference = reference
}
InnerHTMLReference.prototype.toString = function()
{
return this.reference.innerHTML
}
function node()
{
return document.createElement("div")
}
var
root = node(),
first = node(),
second = node(),
echo = node();
body.appendChild(root);
root.appendChild(first);
root.appendChild(second);
echo.innerHTML = "Is there an echo in here?";
first.innerHTML = new InnerHTMLReference(echo);
second.innerHTML = new InnerHTMLReference(echo);
echo.innerHTML = "Not a chance.";
var
constructor = second.innerHTML.constructor.name;
/*
Suprise!
*/
if(constructor != "InnerHTMLReference")
alert("Sorry: " + constructor + " != InnerHTMLReference");
}
</script>
</head>
<body onload = "demonstrate()"/>
</html>
On my platform, this shows:
Is there an echo in here?
Is there an echo in here?
Sorry: String != InnerHTMLReference

Unable to append to jQuery variable

I've been banging my head against this for some time now. I can't find any way of appending this tag to it's parent. The variable seems to be defined any time I check it in a debugger but the append simply doesn't occur. It only seems to work if I do not pass the variable and instead query for the parent inline.
function AdType($parentObj, adSlot)
{
this.$parentObj = $($parentObj);
this.adSlot = adSlot;
}
AdType.prototype.display = function()
{
var insert = document.createElement("ins");
insert.className = "adsbygoogle";
this.$parentObj.append(insert);
};
AdType.prototype.remove = function()
{
this.$parentObj.empty();
};
var Ads = {
mobile : new AdType($("#mobile-ad"), "555"),
display : function () {
if (Display.get() == 'mobile'){
this.mobile.display();
}
}
};
Ads.display();
One thing I notice is that your AdType object is a property of Ads. The codes seem to work when I replace Ads.display() with Ads.mobile.display(). Also, to improve code readability, there is no need to repeat those $ symbol in your function argument.
Take a look at this stack snippet, where I was able to append an <a> tag to your AdType.
function AdType(parentObj, adSlot)
{
this.parentObj = parentObj;
this.adSlot = adSlot;
}
AdType.prototype.display = function()
{
this.parentObj.append('I got appended!');
};
AdType.prototype.remove = function()
{
this.$parentObj.empty();
};
var Ads = {
mobile: new AdType($("#mobile-ad"), "555")
};
Ads.mobile.display();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id ="mobile-ad"></div>

Categories