Change iframe source from another iframe - javascript

Ok, I have 2 iframes inside a parent page (for whatever reason).
I have a navigation menu on parent page, which changes the source of iframe #1...
iFrame #1's job, is to display ANOTHER navigation menu... Like a subnavigation menu...
Now, how can I upon clicking an li inside iFrame #1, change the source of iframe #2? They're both on the same parent page...
Aside from failing miserably, I also get a warning from Chrome's Dev tools -
Unsafe JavaScript attempt to access frame with URL file:///C:/website/index.html from frame with URL file:///C:/website/news/navigator.html. Domains, protocols and ports must match.
Here's some code to make things slightly clearer:
The HTML
<!-- HTML for the parent page itself -->
<iframe id="frameone" src=""></iframe>
<iframe id="frametwo" src=""></iframe>
<button onclick="onenav('test.html')">Change 1st frame</button>
<!-- The following is the HTML that is loaded inside "frameone" -->
<button onclick="twonav('test2.html')">Change 2nd frame</button>
// Javascript
var one = document.getElementById('frameone');
var two = document.getElementById('frametwo');
function onenav(x){
one.src = x;
}
function twonav(y){
two.src = y;
}
To me, this makes sense, since this is all being executed on the parent page... On loading, I query the dev tools and I can see that both, 'one' and 'two' have frame elements... The first button works, the second one, doesn't...

Works for me when using parent.twonav
DEMO
var links = [
'javascript:\'<button onclick="parent.twonav(1)">Change 2nd frame</button>\'',
'javascript:\'Hello\''
];
var one, two;
window.onload=function() {
one = document.getElementById('frameone');
two = document.getElementById('frametwo');
}
function onenav(idx) {
one.src=links[idx];
}
function twonav(idx) {
two.src=links[idx];
}

How did you try to change the iframe source?
parent.document.getElementById('2').src = "the new url";
Did you try something like this? I assumed from your message that the id of the 2nd iframe is 2.

Related

Load javascript in html within content pane

I have an html file that I want to be loaded from various pages into a dijit.contentpane. The content loads fine (I just set the href of the contentpane), but the problem is that javascript within the html file specified by href doesn't seem to be executed at a consistent time.
The final goal of this is to load an html file into a contentpane at an anchor point in the file (i.e. if you typed in index.html#tag in order to jump to a certain part of the file). I've tried a few different methods and can't seem to get anything to work.
What I've tried:
1.
(refering to the href of the dijit.contentpane)
href="page.htm#anchor"
2.
(again, refering to the href of the dijit.contentpane -- didn't really expect this to work, but decided to try anyways)
href="#anchor"
3. (with this last try inside the html specified by href)
<script type="text/javascript">
setTimeout("go_to_anchor();", 2000);
function go_to_anchor()
{
location.href = "#anchor";
}
</script>
This last try was the closest to working of all of them. After 2 seconds (I put the delay there to see if something in the dijit code was possibly loading at the same time as my javascript), I could see the browser briefly jump to the correct place in the html page, but then immediately go back to the top of the page.
Dojo uses hashes in the URL to allow bookmarking of pages loaded through ajax calls.
This is done through the dojo.hash api.
So... I think the best thing you can do is use it to trigger a callback that you write inside your main page.
For scrolling to a given position in your loaded contents, you can then use node.scrollIntoView().
For example, say you have a page with a ContentPane named "mainPane" in which you load an html fragment called "fragment.html", and your fragment contains 2 anchors like this :
-fragment.html :
Anchor 1
<p>some very long contents...</p>
Anchor 2
<p>some very long contents...</p>
Now say you have 2 buttons in the main page (named btn1 and btn2), which will be used to load your fragment and navigate to the proper anchor. You can then wire that up with the following javascript, in your main page :
<script type="text/javascript">
require(['dojo/on',
'dojo/hash',
'dojo/_base/connect',
'dijit/layout/BorderContainer',
'dijit/layout/ContentPane',
'dijit/form/Button'],
function(on, hash, connect){
dojo.ready(function(){
var contentPane = dijit.byId('mainPane');
var btn1 = dijit.byId('btn1');
var btn2 = dijit.byId('btn2');
btn1.on("Click", function(e){
if (!(contentPane.get('href') == 'fragment.html')) {
contentPane.set("href", "fragment.html");
}
hash("anchor1");
});
btn2.on("Click", function(e){
if (!(contentPane.get('href') == 'fragment.html')) {
contentPane.set("href", "fragment.html");
}
hash("anchor2");
});
// In case we have a hash in the URL on the first page load, load the fragment so we can navigate to the anchor.
hash() && contentPane.set("href", "fragment.html");
// This callback is what will perform the actual scroll to the anchor
var callback = function(){
var anchor = Array.pop(dojo.query('a[href="#' + hash() + '"]'));
anchor && anchor.scrollIntoView();
};
contentPane.on("DownloadEnd", function(e){
console.debug("fragment loaded");
// Call the callback the first time the fragment loads then subscribe to hashchange topic
callback();
connect.subscribe("/dojo/hashchange", null, callback);
});
}); // dojo.ready
}); // require
</script>
If the content you're loading contains javascript you should use dojox.layout.ContentPane.

How to move an iFrame in the DOM without losing its state?

Take a look at this simple HTML:
<div id="wrap1">
<iframe id="iframe1"></iframe>
</div>
<div id="warp2">
<iframe id="iframe2"></iframe>
</div>
Let's say I wanted to move the wraps so that the #wrap2 would be before the #wrap1. The iframes are polluted by JavaScript. I am aware of jQuery's .insertAfter() and .insertBefore(). However, when I use those, the iFrame loses all of its HTML, and JavaScript variables and events.
Lets say the following was the iFrame's HTML:
<html>
<head>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
// The variable below would change on click
// This represents changes on variables after the code is loaded
// These changes should remain after the iFrame is moved
variableThatChanges = false;
$(function(){
$("body").click(function(){
variableThatChanges = true;
});
});
</script>
</head>
<body>
<div id='anything'>Illustrative Example</div>
</body>
</html>
In the above code, the variable variableThatChanges would...change if the user clicked on the body. This variable, and the click event, should remain after the iFrame is moved (along with any other variables/events that have been started)
My question is the following: with JavaScript (with or without jQuery), how can I move the wrap nodes in the DOM (and their iframe childs) so that the iFrame's window stays the same, and the iFrame's events/variables/etc stay the same?
It isn't possible to move an iframe from one place in the dom to another without it reloading.
Here is an example to show that even using native JavaScript the iFrames still reload:
http://jsfiddle.net/pZ23B/
var wrap1 = document.getElementById('wrap1');
var wrap2 = document.getElementById('wrap2');
setTimeout(function(){
document.getElementsByTagName('body')[0].appendChild(wrap1);
},10000);
This answer is related to the bounty by #djechlin
A lot of search on the w3/dom specs and didn't find anything final that specifically says that iframe should be reloaded while moving in the DOM tree, however I did find lots of references and comments in the webkit's trac/bugzilla/microsoft regarding different behavior changes over the years.
I hope someone will find anything specific regarding this issue, but for now here are my findings:
According to Ryosuke Niwa - "That's the expected behavior".
There was a "magic iframe" (webkit, 2010), but it was removed in 2012.
According to MS - "iframe resources are freed when removed from the DOM". When you appendChild(node) of existing node - that node is first removed from the dom.
Interesting thing here - IE<=8 didn't reload the iframe - this behavior is (somewhat) new (since IE>=9).
According to Hallvord R. M. Steen comment, this is a quote from the iframe specs
When an iframe element is inserted into a document that has a browsing context, the user agent must create a new browsing context, set the element's nested browsing context to the newly-created browsing context, and then process the iframe attributes for the "first time".
This is the most close thing I found in the specs, however it's still require some interpretation (since when we move the iframe element in the DOM we don't really do a full remove, even if the browsers uses the node.removeChild method).
Whenever an iframe is appended and has a src attribute applied it fires a load action similarly to when creating an Image tag via JS. So when you remove and then append them they are completely new entities and they refresh. Its kind of how window.location = window.location will reload a page.
The only way I know to reposition iframes is via CSS. Here is an example I put together showing one way to handle this with flex-box:
https://jsfiddle.net/3g73sz3k/15/
The basic idea is to create a flex-box wrapper and then define an specific order for the iframes using the order attribute on each iframe wrapper.
<style>
.container{
display: flex;
flex-direction: column;
}
</style>
<div class="container">
<div id="wrap1" style="order: 0" class="iframe-wrapper">
<iframe id="iframe1" src="https://google.com"></iframe>
</div>
<div id="warp2" style="order: 1" class="iframe-wrapper">
<iframe id="iframe2" src="https://bing.com"></iframe>
</div>
</div>
As you can see in the JS fiddle these order styles are inline to simplify the flip button so rotate the iframes.
I sourced the solution from this StackOverflow question: Swap DIV position with CSS only
Hope that helps.
If you have created the iFrame on the page and simply need to move it's position later try this approach:
Append the iFrame to the body and use a high z-index and top,left,width,height to put the iFrame where you want.
Even CSS zoom works on the body without reloading which is awesome!
I maintain two states for my "widget" and it is either injected in place in the DOM or to the body using this method.
This is useful when other content or libraries will squish or squash your iFrame.
BOOM!
Unfortunately, the parentNode property of an HTML DOM element is read-only. You can adjust the positions of the iframes, of course, but you can't change their location in the DOM and preserve their states.
See this jsfiddle I created that provides a good test bed. http://jsfiddle.net/RpHTj/1/
Click on the box to toggle the value. Click on the "move" to run the javascript.
This question is pretty old... but I did find a way to move an iframe without it reloading. CSS only. I have multiple iframes with camera streams, I dont like when they reload when i swap them. So i used a combination of float, position:absolute, and some dummy blocks to move them around without reloading them and having the desired layout on demand (resizing and all).
If you are using the iframe to access pages you control, you could create some javascript to allow your parent to communicate with the iframe via postMessage
From there, you could build login inside the iframe to record state changes, and before moving dom, request that as a json object.
Once moved, the iframe will reload, you can pass the state data into the iframe and the iframe listening can parse the data back into the previous state.
PaulSCoder has the right solution. Never manipulate the DOM for this purpose. The classic approach for this is to have a relative position and "flip" the positions in the click event. It's only not wise to put the click event on the body, because it bubbles from other elements too.
$("body").click(function () {
var frame1Height = $(frame1).outerHeight(true);
var frame2Height = $(frame2).outerHeight(true);
var pos = $(frame1).css("top");
if (pos === "0px") {
$(frame1).css("top", frame2Height);
$(frame2).css("top", -frame1Height);
} else {
$(frame1).css("top", 0);
$(frame2).css("top", 0);
}
});
If you only have content that is not cross-domain you could save and restore the HTML:
var htmlContent = $(frame).contents().find("html").children();
// do something
$(frame).contents().find("html").html(htmlContent);
The advantage of the first method is, that the frame keeps on doing what it was doing. With the second method, the frame gets reloaded and starts it's code again.
At least in some circumstances a shadow dom with slotting might be an option.
<template>
<style>div {outline:1px solid black; height:45px}</style>
<div><slot name="a" /></div>
<div><slot name="b" /></div>
</template>
<div id="shadowhost">
<iframe src="data:text/html,<button onclick='this.innerText+=`!`'>!</button>"
slot="a" height=40px ></iframe>
</div>
<button onclick="ifr.slot= (ifr.slot=='a') ? 'b' : 'a';">swap</button>
<script>
document.querySelector('#shadowhost').attachShadow({mode: 'open'}).appendChild(
document.querySelector('template').content
);
ifr=document.querySelector('iframe');
</script>
In response to the bounty #djechlin placed on this question, I have forked the jsfiddle posted by #matt-h and have come to the conclusion that this is still not possible.
http://jsfiddle.net/gr3wo9u6/
//this does not work, the frames reload when appended back to the DOM
function swapFrames() {
var w1 = document.getElementById('wrap1');
var w2 = document.getElementById('wrap2');
var f1 = w1.querySelector('iframe');
var f2 = w2.querySelector('iframe');
w1.removeChild(f1);
w2.removeChild(f2);
w1.appendChild(f2);
w2.appendChild(f1);
//f1.parentNode = w2;
//f2.parentNode = w1;
//alert(f1.parentNode.id);
}

Invoking a function in an iframe from the parent window

I can't figure out what I'm doing wrong here.
I am building a website with no server-side
I have a main page with an iframe and on a button click I want the iframe's src to change and a function in it to be invoked with a passed parameter.
The function is not called for some reason.
here's my code:
the iframe:
<iframe id="main_area_frame" name="main_area_frame" src="" frameborder="0" width="100%" height="100%"></iframe>
the onclick function:
function onSubMenuClick(images)
{
//Set Images Frame
main_area = document.getElementById("main_area_frame");
main_area.src = "ImagesFrame.html";
main_area.contentWindow.initializeImages(images);
}
the function in the iframe(ImagesFrame.html):
function initializeImages(imagesStr)
{
alert("initializeImages");
...
}
some weird things I noticed are that
when adding an alert just before main_area.contentWindow.initializeImages(images);
the function is somehow called successfully.
if I set the iframe's src from the begining and skip the line main_area.src = "ImagesFrame.html"; - the function is again, called.
any ideas?
Not sure if that is the cause, but I would try putting a delay(sleep) between
main_area.src = "ImagesFrame.html";
and
main_area.contentWindow.initializeImages(images);
in order to allow the iframe to be rendered (I do not know if it is the rendered by the same frame or the browser launches a new one).
Just my two cents.
I have had success with publishing the function from the child iframe during
onload handling. For example, (substituting your funcName):
In child iframe, edit body tag (or use equivalent javascript)
<body onload='top.funcName = funcName'>
In parent page, edit javascript; now the
funcName will be generally accessible as,
value = top.funcName( )

Show part of page in new window

I'd like to open the page in the image below, but only showing the green part in the new window. Hiding the menu and the header to the user.
function openNewWindow() {
var pr = window.open("Page.aspx", "page", "width=700, height=400");
pr.onload() = function() {
pr.document.getElementById("header").style.display = 'none';
}
}
Is it possible to set some kind of offset for the page in the new window? Like left:-40px and top:-20px or something similar? I know top and left positions the new window rather than its content, but is there something I can do to change the position of the actual content?
Is there a work-around or another solution with the same result?
EDIT
When I click Click I want Page.aspx (image above) to open in a new window, but without menu and header showing.
how about you open a page that shows an iframe which loads your page -- and then you can set your iframe width/height to what you need and whether to provide scrolling or not?
something like this:
<html>
<!-- this is page2.aspx -->
<body>
<!-- header -->
<!-- menu -->
<iframe id="abc"...></iframe>
<script type="text/javascript">
var page = ... //retrieve the value of the parameter "url" passed to us (you can find how to do this by googling)
document.getElementById( "abc" ).src = page; //set the iframe url to the parameter passed
</script>
</body>
</html>
then your function becomes:
function openNewWindow() {
window.open("Page.aspx?url=http://page/to/load", "page", "width=700, height=400");
}
Load the whole page, but hide the header and menu using Javascript:
newwindow.onload = function() {
newwindow.document.getElemementById('header').style.display = 'none';
newwindow.document.getElemementById('menu').style.display = 'none';
}
(or use JQuery's .hide() method)
Load the whole page, but add an extra stylesheet which sets the header and menu to hidden:
#header, #menu {display:none !important;}
when you serve the page, use a different template which doesn't include the header and menu, etc. All things being equal, this would probably be the best option, but I can't really give any advice on this without knowing a whole load more about your code.
(all of the above assumes that you have the IDs in your header and menu that I've specified; change as appropriate)
Agree with Spudley, but if that's not possible you might be able to get by with negative margins. Like this:
body { margin: -50px 0 0 -50px }

Add HTML on an injected iframe

I'm currently developing a Toolbar from Google Chrome. Basically it's a toolbar that i'm injecting in every web pages by using a Content-Script. Technically the toolbar is materializd by a iframe that include all the components like button, dropMenu,... Here is the script you make this :
// Take down the webPage
document.getElementsByTagName('body')[0].style.marginTop = '39px';
var body = $('body'),
toolbarURL = chrome.extension.getURL("yourtoolbar.html"),
iframe = $('<iframe id="YourToolbarFrame" scrolling="no" src="'+toolbarURL+'">');
// Insertion
body.append(iframe);
// Effect
$("#YourToolbarFrame").hide().fadeIn(800);
But right now i'm trying to add some component on this iframe for example a button but it didn't work...
var yt = $("#YourToolbarFrame");
var newButton = $('<input type="image" src="images/pop.ico" name="InstantMessage" width="23" height="23">');
yt.append(newButton);
The body of the iframe look like this :
<body>
<div class="default">
// COMPONENTS
</div>
</body>
Hope someone can provide me some help ! :)
You have to wait until the iframe loaded. E.g.:
iframe.load(function() {
var newButton = ...;
$(this).contents().find('body').append(newButton);
}).appendTo('body');
Not sure how Chrome handles the same-origin policy for content scripts though.
Since you're using jQuery, you could try using
$('#YourToolbarFrame').contents().find('body').append(newButton);
Or if you don't want to append to the body directly, use any other element in the find() statement.

Categories