Access parent frame from IFRAME using JavaScript [duplicate] - javascript

Well, I have an IFrame, which calls a same domain page.
My problem is that I want to access some information from this parent Iframe from this called page (from JavaScript). How can I access this Iframe?
Details: There are several Iframes just like this one, that can have the same page loaded, because I am programming a Windows environment. I intend to close this Iframe, that's why I need to know which I should close from inside him. I have an array keeping references to these Iframes.
EDIT: There iframes are generated dynamically

Also you can set name and ID to equal values
<iframe id="frame1" name="frame1" src="any.html"></iframe>
so you will be able to use next code inside child page
parent.document.getElementById(window.name);

Simply call window.frameElement from your framed page.
If the page is not in a frame then frameElement will be null.
The other way (getting the window element inside a frame is less trivial) but for sake of completeness:
/**
* #param f, iframe or frame element
* #return Window object inside the given frame
* #effect will append f to document.body if f not yet part of the DOM
* #see Window.frameElement
* #usage myFrame.document = getFramedWindow(myFrame).document;
*/
function getFramedWindow(f)
{
if(f.parentNode == null)
f = document.body.appendChild(f);
var w = (f.contentWindow || f.contentDocument);
if(w && w.nodeType && w.nodeType==9)
w = (w.defaultView || w.parentWindow);
return w;
}

I would recommend using the postMessage API.
In your iframe, call:
window.parent.postMessage({message: 'Hello world'}, 'http://localhost/');
In the page you're including the iframe you can listen for events like this:
window.addEventListener('message', function(event) {
if(event.origin === 'http://localhost/')
{
alert('Received message: ' + event.data.message);
}
else
{
alert('Origin not allowed!');
}
}, false);
By the way, it is also possible to do calls to other windows, and not only iframes.
Read more about the postMessage API on John Resigs blog here

Old question, but I just had this same issue and found a way to get the iframe. It's simply a matter of iterating through the parent window's frames[] array and testing each frame's contentWindow against the window in which your code is running. Example:
var arrFrames = parent.document.getElementsByTagName("IFRAME");
for (var i = 0; i < arrFrames.length; i++) {
if (arrFrames[i].contentWindow === window) alert("yay!");
}
Or, using jQuery:
parent.$("iframe").each(function(iel, el) {
if(el.contentWindow === window) alert("got it");
});
This method saves assigning an ID to each iframe, which is good in your case as they are dynamically created. I couldn't find a more direct way, since you can't get the iframe element using window.parent - it goes straight to the parent window element (skipping the iframe). So looping through them seems the only way, unless you want to use IDs.

you can use parent to access the parent page. So to access a function it would be:
var obj = parent.getElementById('foo');

Once id of iframe is set, you can access iframe from inner document as shown below.
var iframe = parent.document.getElementById(frameElement.id);
Works well in IE, Chrome and FF.

Maybe just use
window.parent
into your iframe to get the calling frame / windows. If you had multiple calling frame, you can use
window.top

Try this, in your parent frame set up you IFRAMEs like this:
<iframe id="frame1" src="inner.html#frame1"></iframe>
<iframe id="frame2" src="inner.html#frame2"></iframe>
<iframe id="frame3" src="inner.html#frame3"></iframe>
Note that the id of each frame is passed as an anchor in the src.
then in your inner html you can access the id of the frame it is loaded in via location.hash:
<button onclick="alert('I am frame: ' + location.hash.substr(1))">Who Am I?</button>
then you can access parent.document.getElementById() to access the iframe tag from inside the iframe

// just in case some one is searching for a solution
function get_parent_frame_dom_element(win)
{
win = (win || window);
var parentJQuery = window.parent.jQuery;
var ifrms = parentJQuery("iframe.upload_iframe");
for (var i = 0; i < ifrms.length; i++)
{
if (ifrms[i].contentDocument === win.document)
return ifrms[i];
}
return null;
}

Related

QuerySelector for Web Elements Inside iframe

Edit: New title.
What I'm looking for is a document.querySelector for elements inside an iframe.
I've done quite a bit of Googling for an answer and finally I'm stumped.
I'm trying to query inside an iframe. I'm building string selectors to be used in Selenium and usually I just inspect the element with Firebug, and use document.querySelectorAll("theStringIBuid");
But it doesn't work with elements inside iframes. I've tried all of the below to get an element "radiobutton1" inside the "page-iframe" iframe.
var elem1 = ".page-iframe";
console.log(elem1);
var elem2 = ".radiobutton1";
console.log(elem2);
document.querySelectorAll(elem1+elem2+"");
document.querySelectorAll('.page-iframe').contentWindow.document.body.querySelectorAll('.radiobutton1')
document.getElementById('.page-iframe').contentWindow.document.body.innerHTML;
[].forEach.call( document.querySelectorAll('.page-iframe'),
function fn(elem){
console.log(elem.contentWindow.document.body.querySelectorAll('.radiobutton1')); });
var contentWindow = document.getElementById('.page-iframe').contentWindow
var contentWindow = document.querySelectorAll('.page-iframe')
var contentWindow = document.querySelectorAll('.page-iframe')[0].contentWindow
Thanks-
simple es6 adapted from h3manth:
document.querySelectorAll('iframe').forEach( item =>
console.log(item.contentWindow.document.body.querySelectorAll('a'))
)
if the original page's url isn't at the same domain with the iframe content, the javascript will treat the iframe as a black box, meaning it will not see anything inside it.
You can do this:
document.querySelector("iframe").contentWindow.document.querySelector("button")
https://m.youtube.com/watch?v=-mNp3-UX9Qc
You can simply use
document.querySelector("iframe").contentDocument.body.querySelector("#btn")
First query selector is to select the iframe. Then we can access ifram dom using content document and use the 2nd query selector to select the element inside iframe.
Here's a snippet for diving into same-origin frames (ie-compatible ES5):
function findInFramesRec(selector, doc) {
var hit = doc.querySelector(selector);
if (hit) return hit;
var frames = Array.prototype.slice.call(doc.frames);
for(var i = 0; (i < frames.length) && !hit ; i++) {
try {
if (!frames[i] || !frames[i].document) continue;
hit = findInFramesRec(selector, frames[i].document);
} catch(e) {}
}
return hit;
}
This dives into both frameset frames and iframes alike. It may even survive (though not enter) cross origin frames.

How to share a data between a window and a frame in JavaScript

This is WebKit browsers specific (meaning that I only need to make it work in WebKit specific, i.e. iOS/Android browsers, but I'm testing in Chrome).
I have a page. The page loads one or more iframes, with contents from another domain. I need to receive messages (using postMessage()) from these iframes, and I need to be able to identify which iframe a specific message came from.
I can't find a way to do that that does not involve throwing something the iframe URL that the iframe contents then can pass back to me. I would like to not have to meddle with the URL, as there is no guarantee I can safely do that (redirects can throw the parameters out, for example).
I tried something that I thought was reasonable. When I create the iframe element (it's done from J/S), I associated a property with the element, let's say 'shared_secret'. When I get the message event back from the frame, I tried to locating the element that the calling frame was created with, and reading that property.
function onMessage(evt) {
var callerId = evt.source.frameElement.shared_secret;
// ....
}
window.addEventListener(message, onMessage);
var frameEl = document.createElement('iframe');
frameEl.shared_secret = 'sommething blue';
frameEl.src = 'http://aliens.com/my.html';
somewhereInMyDoc.appendChild(frameEl);
When the frame loads, it will run:
window.parent.postMessage('do you know who I am?', '*');
However, frameElement turns out undefined in the above onMessage(). I guess for the security reasons, it does work perfectly when the parent/child are from the same domain.
And it's actually ironic. Parent window can not access event.source.frameElement because event.source is an alien window. iFrame window can not call window.frameElement, because frameElement is in an alien window. So nobody can get access to it.
So, is there something that I can use as a token that I can set on a newly loaded frame, and somehow get back?
Thank you.
For people looking for some code, here is what I used to find the iframe who sent the message :
/**
* Returns the iframe corresponding to a message event or null if not found.
*
* 'e' is the event object
*/
function getFrameTarget (e) {
var frames = document.getElementsByTagName('iframe'),
frameId = 0,
framesLength = frames.length;
for (; frameId < framesLength; frameId++) {
if (frames[frameId].contentWindow === e.source) {
return frames[frameId];
}
}
return null;
}
Thanks to Pawel Veselov and DMoses !
This should be credited to https://stackoverflow.com/users/695461/dmoses.
You can actually compare the content window object of the frame element to the event.source of the message event, and the comparison will yield TRUE if they are, in fact, the same.
So, to solve my particular problem, I'll need to keep the list of frame elements that I've created (sprinkling them, if needed, with whatever additional properties), and when the event comes in, iterating through all, looking for one that has its contentWindow property equal to the event.source property.
UPDATE
We did, through encountering some nasty bugs, also found out that you should put an 'id' on the iframe created from within the parent window. Especially if that window is itself in an iframe. Otherwise, certain (Android 4.x being known for sure) browsers will yield true comparison even if the message is being received from a completely different child frame.

Reading Iframe Content in Iframe Load

I just wanna learn how to read Iframe content in onLoad event of iframe and write it's content to the main page ? Thanks..
You can use this jquery function in parent page and it also supports many other function to add and remove stuffs:
<script type="text/javascript">
function myFunction() {
var $currIFrame = $('#Frame1');
$currIFrame.contents().find("body #idofElement").val('Value i want to set');
}
</script>
In above code: #idofElement is the element from HTML page in iframe whose value you want to set. I Hope it helps You..
I have struggled with same this past day. It seems to matter how you access the iframe. If you use document.getElementById() you get an Iframe object, which has no onload event. However, if you access through window.frames[] array, for example
var iframeWindow = top.frames['iframeID'],
you get a window object, which does have onload event.
(ie uses the frame id attribute , but ff uses name attribute. So use both and make the same)
You can then assign
iframeWindow.onload=function(){iframeContent=iframeWindow.document.body.innerHTML;};
Notice, since iframeWindow is a window object, you use window syntax to access content.
jQuery("#xiframe").load(function()
{
var doc = null;
try{
doc = this.document || this.contentDocument || this.contentWindow && xiframe.contentWindow.document || null;
} catch(err) {
alert('error: ' + err.description);
}
try {
if(!doc) {
alert('error');
return false;
}
} catch(err) {
alert('error: ' + err.description);
return false;
}
alert(String(jQuery(doc.body).html());
}
document.getElementById('iframeID').contentWindow.onload = function(){
top.document.innerHTML = this.document.innerHTML;
}
parent document (the page on which the iframe is displayed)
child document (the page displayed inside the iframe)
If these two documents are in the same domain, you can use the window.parent.document object in the child document's onload event handler to access the parent document.

How to identify if a webpage is being loaded inside an iframe or directly into the browser window?

I am writing an iframe based facebook app. Now I want to use the same html page to render the normal website as well as the canvas page within facebook. I want to know if I can determine whether the page has been loaded inside the iframe or directly in the browser?
Browsers can block access to window.top due to same origin policy. IE bugs also take place. Here's the working code:
function inIframe () {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
top and self are both window objects (along with parent), so you're seeing if your window is the top window.
When in an iframe on the same origin as the parent, the window.frameElement method returns the element (e.g. iframe or object) in which the window is embedded. Otherwise, if browsing in a top-level context, or if the parent and the child frame have different origins, it will evaluate to null.
window.frameElement
? 'embedded in iframe or object'
: 'not embedded or cross-origin'
This is an HTML Standard with basic support in all modern browsers.
if ( window !== window.parent )
{
// The page is in an iframe
}
else
{
// The page is not in an iframe
}
I'm not sure how this example works for older Web browsers but I use this for IE, Firefox and Chrome without an issue:
var iFrameDetection = (window === window.parent) ? false : true;
RoBorg is correct, but I wanted to add a side note.
In IE7/IE8 when Microsoft added Tabs to their browser they broke one thing that will cause havoc with your JS if you are not careful.
Imagine this page layout:
MainPage.html
IframedPage1.html (named "foo")
IframedPage2.html (named "bar")
IframedPage3.html (named "baz")
Now in frame "baz" you click a link (no target, loads in the "baz" frame) it works fine.
If the page that gets loaded, lets call it special.html, uses JS to check if "it" has a parent frame named "bar" it will return true (expected).
Now lets say that the special.html page when it loads, checks the parent frame (for existence and its name, and if it is "bar" it reloads itself in the bar frame. e.g.
if(window.parent && window.parent.name == 'bar'){
window.parent.location = self.location;
}
So far so good. Now comes the bug.
Lets say instead of clicking on the original link like normal, and loading the special.html page in the "baz" frame, you middle-clicked it or chose to open it in a new Tab.
When that new tab loads (with no parent frames at all!) IE will enter an endless loop of page loading! because IE "copies over" the frame structure in JavaScript such that the new tab DOES have a parent, and that parent HAS the name "bar".
The good news, is that checking:
if(self == top){
//this returns true!
}
in that new tab does return true, and thus you can test for this odd condition.
The accepted answer didn't work for me inside the content script of a Firefox 6.0 Extension (Addon-SDK 1.0): Firefox executes the content script in each: the top-level window and in all iframes.
Inside the content script I get the following results:
(window !== window.top) : false
(window.self !== window.top) : true
The strange thing about this output is that it's always the same regardless whether the code is run inside an iframe or the top-level window.
On the other hand Google Chrome seems to execute my content script only once within the top-level window, so the above wouldn't work at all.
What finally worked for me in a content script in both browsers is this:
console.log(window.frames.length + ':' + parent.frames.length);
Without iframes this prints 0:0, in a top-level window containing one frame it prints 1:1, and in the only iframe of a document it prints 0:1.
This allows my extension to determine in both browsers if there are any iframes present, and additionally in Firefox if it is run inside one of the iframes.
I'm using this:
var isIframe = (self.frameElement && (self.frameElement+"").indexOf("HTMLIFrameElement") > -1);
Use this javascript function as an example on how to accomplish this.
function isNoIframeOrIframeInMyHost() {
// Validation: it must be loaded as the top page, or if it is loaded in an iframe
// then it must be embedded in my own domain.
// Info: IF top.location.href is not accessible THEN it is embedded in an iframe
// and the domains are different.
var myresult = true;
try {
var tophref = top.location.href;
var tophostname = top.location.hostname.toString();
var myhref = location.href;
if (tophref === myhref) {
myresult = true;
} else if (tophostname !== "www.yourdomain.com") {
myresult = false;
}
} catch (error) {
// error is a permission error that top.location.href is not accessible
// (which means parent domain <> iframe domain)!
myresult = false;
}
return myresult;
}
Best-for-now Legacy Browser Frame Breaking Script
The other solutions did not worked for me. This one works on all browsers:
One way to defend against clickjacking is to include a "frame-breaker" script in each page that should not be framed. The following methodology will prevent a webpage from being framed even in legacy browsers, that do not support the X-Frame-Options-Header.
In the document HEAD element, add the following:
<style id="antiClickjack">body{display:none !important;}</style>
First apply an ID to the style element itself:
<script type="text/javascript">
if (self === top) {
var antiClickjack = document.getElementById("antiClickjack");
antiClickjack.parentNode.removeChild(antiClickjack);
} else {
top.location = self.location;
}
</script>
This way, everything can be in the document HEAD and you only need one method/taglib in your API.
Reference: https://www.codemagi.com/blog/post/194
I actually used to check window.parent and it worked for me, but lately window is a cyclic object and always has a parent key, iframe or no iframe.
As the comments suggest hard comparing with window.parent works. Not sure if this will work if iframe is exactly the same webpage as parent.
window === window.parent;
Since you are asking in the context of a facebook app, you might want to consider detecting this at the server when the initial request is made. Facebook will pass along a bunch of querystring data including the fb_sig_user key if it is called from an iframe.
Since you probably need to check and use this data anyway in your app, use it to determine the the appropriate context to render.
function amiLoadedInIFrame() {
try {
// Introduce a new propery in window.top
window.top.dummyAttribute = true;
// If window.dummyAttribute is there.. then window and window.top are same intances
return !window.dummyAttribute;
} catch(e) {
// Exception will be raised when the top is in different domain
return true;
}
}
Following on what #magnoz was saying, here is a code implementation of his answer.
constructor() {
let windowLen = window.frames.length;
let parentLen = parent.frames.length;
if (windowLen == 0 && parentLen >= 1) {
this.isInIframe = true
console.log('Is in Iframe!')
} else {
console.log('Is in main window!')
}
}
It's an ancient piece of code that I've used a few times:
if (parent.location.href == self.location.href) {
window.location.href = 'https://www.facebook.com/pagename?v=app_1357902468';
}
If you want to know if the user is accessing your app from facebook page tab or canvas check for the Signed Request. If you don't get it, probably the user is not accessing from facebook.
To make sure confirm the signed_request fields structure and fields content.
With the php-sdk you can get the Signed Request like this:
$signed_request = $facebook->getSignedRequest();
You can read more about Signed Request here:
https://developers.facebook.com/docs/reference/php/facebook-getSignedRequest/
and here:
https://developers.facebook.com/docs/reference/login/signed-request/
This ended being the simplest solution for me.
<p id="demofsdfsdfs"></p>
<script>
if(window.self !== window.top) {
//run this code if in an iframe
document.getElementById("demofsdfsdfs").innerHTML = "in frame";
}else{
//run code if not in an iframe
document.getElementById("demofsdfsdfs").innerHTML = "no frame";
}
</script>
if (window.frames.length != parent.frames.length) { page loaded in iframe }
But only if number of iframes differs in your page and page who are loading you in iframe. Make no iframe in your page to have 100% guarantee of result of this code
Write this javascript in each page
if (self == top)
{ window.location = "Home.aspx"; }
Then it will automatically redirects to home page.

Pass value to iframe from a window

I need to send a value to an iframe.
The iframe is present within the current window. How can I achieve this?
I need to do it with javascript in the parent window that contains the iframe.
First, you need to understand that you have two documents: The frame and the container (which contains the frame).
The main obstacle with manipulating the frame from the container is that the frame loads asynchronously. You can't simply access it any time, you must know when it has finished loading. So you need a trick. The usual solution is to use window.parent in the frame to get "up" (into the document which contains the iframe tag).
Now you can call any method in the container document. This method can manipulate the frame (for example call some JavaScript in the frame with the parameters you need). To know when to call the method, you have two options:
Call it from body.onload of the frame.
Put a script element as the last thing into the HTML content of the frame where you call the method of the container (left as an exercise for the reader).
So the frame looks like this:
<script>
function init() { window.parent.setUpFrame(); return true; }
function yourMethod(arg) { ... }
</script>
<body onload="init();">...</body>
And the container like this:
<script>
function setUpFrame() {
var frame = window.frames['frame-id'].contentWindow;
frame.yourMethod('hello');
}
</script>
<body><iframe name="frame-id" src="..."></iframe></body>
Depends on your specific situation, but if the iframe can be deployed after the rest of the page's loading, you can simply use a query string, a la:
<iframe src="some_page.html?somedata=5&more=bacon"></iframe>
And then somewhere in some_page.html:
<script>
var params = location.href.split('?')[1].split('&');
data = {};
for (x in params)
{
data[params[x].split('=')[0]] = params[x].split('=')[1];
}
</script>
Here's another solution, usable if the frames are from different domains.
var frame = /*the iframe DOM object*/;
frame.contentWindow.postMessage({call:'sendValue', value: /*value*/}, /*frame domain url or '*'*/);
And in the frame itself:
window.addEventListener('message', function(event) {
var origin = event.origin || event.originalEvent.origin; // For Chrome, the origin property is in the event.originalEvent object.
if (origin !== /*the container's domain url*/)
return;
if (typeof event.data == 'object' && event.data.call=='sendValue') {
// Do something with event.data.value;
}
}, false);
Don't know which browsers support this, though.
Use the frames collection.
From the link:
var frames = window.frames; // or // var frames = window.parent.frames;
for (var i = 0; i < frames.length; i++) {
// do something with each subframe as frames[i]
frames[i].document.body.style.background = "red";
}
If the iframe has a name you may also do the following:
window.frames['ponies'].number_of_ponies = 7;
You can only do this if the two pages are served from the same domain.
Two more options, which are not the most elegant but probably easier to understand and implement, especially in case the data that the iframe needs from its parent is just a few vars, not complex objects:
Using the URL Fragment Identifier (#)
In the container:
<iframe name="frame-id" src="http://url_to_iframe#dataToFrame"></iframe>
In the iFrame:
<script>
var dataFromDocument = location.hash.replace(/#/, "");
alert(dataFromDocument); //alerts "dataToFrame"
</script>
Use the iFrame's name
(I don't like this solution - it's abusing the name attribute, but it's an option so I'm mentioning it for the record)
In the container:
<iframe name="dataToFrame" src="http://url_to_iframe"></iframe>
In the iFrame:
<script type="text/javascript">
alert(window.name); // alerts "dataToFrame"
</script>
We can use "postMessage" concept for sending data to an underlying iframe from main window.
you can checkout more about postMessage using this link
add the below code inside main window page
// main window code
window.frames['myFrame'].contentWindow.postMessage("Hello World!");
we will pass "Hello World!" message to an iframe contentWindow with iframe id="myFrame".
now add the below code inside iframe source code
// iframe document code
function receive(event) {
console.log("Received Message : " + event.data);
}
window.addEventListener('message', receive);
in iframe webpage we will attach an event listener to receive event and in 'receive' callback we will print the data to console
Incase you're using angular and an iframe inside, you'll need need to listen to the iframe to finish loading. You can do something like this:
document.getElementsByTagName("iframe")[0].addEventListener('load', () => {
document.getElementsByTagName("iframe")[0].contentWindow.postMessage(
{
call: 'sendValue',
value: 'data'
},
window.location.origin)
})
You will have to get the iframe one way or another (there are better ways to do it in angular) and then wait for it to load. Or else the listener won't be attached to it even if you do it inside lifecycle methods like ngAfterViewInit()
Have a look at the link below, which suggests it is possible to alter the contents of an iFrame within your page with Javascript, although you are most likely to run into a few cross browser issues. If you can do this you can use the javascript in your page to add hidden dom elements to the iFrame containing your values, which the iFrame can read.
Accessing the document inside an iFrame
Just another way.
From iframe you can add event listeners and dispatch events into parent document:
parent.document.addEventListener('iframe-event', (e) => {
console.log('iframe-event', e.detail);
});
setTimeout(() => {
console.log('dispatchEvent from iframe');
const event = new CustomEvent('frame-ready', { detail: 'parent event dispatched from iframe' });
parent.document.dispatchEvent(event);
}, 1000);
And from parent you can add event listeners and dispatch events in its own document:
document.addEventListener('frame-ready', (e) => {
const event = new CustomEvent('iframe-event', { detail: 'iframe event dispatched from parent' });
document.dispatchEvent(event);
});
Also if in any case you need to open this frame in a new tab you can still use events for communication.
From your frame/tab:
if (opener) {
const event = new CustomEvent('frame-ready', { detail: 'parent event dispatched from new tab' });
opener.document.dispatchEvent(event);
}
From your parent/opener:
window.open('my-frame.html', '_blank');
document.addEventListener('frame-ready', (e) => {
const event = new CustomEvent('iframe-event', { detail: 'iframe event dispatched from parent' });
document.dispatchEvent(event);
});
Just be aware that window.open will expose your DOM to next page it opens, so if you use it to open any third part url, you must always use rel=noopener to avoid security issues:
window.open('third-part-url.html', '_blank', 'noopener');
In your main homepage, add this line-
window.postMessage("Hello data from Homepage");
Inside your iframe , add this line-
window.addEventListener("message", receiveDataFromWeb);
const receiveDataFromWeb= (data)=> {
console.log(data);
//this should print Hello data from Homepage
}
What you have to do is to append the values as parameters in the iframe src (URL).
E.g. <iframe src="some_page.php?somedata=5&more=bacon"></iframe>
And then in some_page.php file you use php $_GET['somedata'] to retrieve it from the iframe URL. NB: Iframes run as a separate browser window in your file.

Categories