access function in iframe from cross domain parent - javascript

I have done some research on this and what i found was in same domain's case you can use
document.getElementById('iframe_id').contentWindow.function_name()
I tried this on cross-domain (just experimenting)
It gave a cross domain error &
Exception: TypeError: Property 'function_name' of object [object Window] is not a function
So I have a three part question
a) It is understandable if I have trouble accessing parent from child (and I am already handling this, using another iframe(from parent's domain) within child iframe).
But why do we have issue accessing child from parent, isn't that the same thing as having a
b) When I debug, using the chrome inspector tool & try to see the value of document.getElementById('iframe_id').contentWindow, I see my function_name under it, but cannot access it by document.getElementById('iframe_id').contentWindow.function_name(). Why is that?
c) How do I successfully call a javascript function in my iframe from the parent(without easyXDM or any other plugin)?
More details:
I am more of looking for two way communication as in, my parent will call a function in iframe that returns a result & based on that result the parent will decide if it wants to redirect or not

I used postMessage. Seemed like a simpler solution.
What I wanted to do:
'beforeunload', send a message to child iframe, receive a response as callback. based on response show message or navigate.
But this was not possible.
This is how I solved it:
Every time a change was made in my child frame, I sent a message to the parent.
I collected these messages & used them on 'beforeunload' to decide if i should navigate or show the message

You can use location.hash polling. Where your parent changes the URL #hash and the child checks for changes in a setInterval loop. http://ajaxian.com/archives/cross-domain-iframe-communication-without-location-polling
You'd be essentially reinventing the wheel with regards to "easyXDM".

Related

Sending cross-origin iFrame content over postMessage fails

First of all, I know that the topic of accessing iFrame Elements cross-domain is a tricky topic and I might be going with this nowhere.
I have a Google Forms embedded in my app that I need to set up in a way to tell me when a user has submitted his/her response. I already tried lots of things but the most optimistic way that could work would be just to read the HTML of the iFrame when the last page has been loaded saying that the response was submitted.
Therefore, I was looking for solutions on how to simply read any kind of snippet of the iFrame's content and I came across this comment in another thread:
https://stackoverflow.com/a/32265508/3856569
I'm trying reading the content of the iFrame as suggested in the comment and sending it to the parent windows via postMessage like so:
$(document).ready(function() {
document.getElementById("googleForm").addEventListener("load",
function() {
var message = document.getElementById("googleForm");
parent.postMessage(JSON.parse(JSON.stringify(message)), '*')
});
})
and the parent-window reads the message like so:
function receiveMessageGoogleForm (event) {
console.log(event)
}
//Listen for message events
window.addEventListener("message", receiveMessageGoogleForm, false);
However, the data property of the event object upon receiving the message seems to be empty.
Is this another inbuilt mechanism to avoid reading any kind of a cross-origin iFrame or am I missing something here?
Forget about postMessage for a moment.
var message = document.getElementById("googleForm");
var result = JSON.parse(JSON.stringify(message));
console.log(result);
<div id="googleForm">content</div>
If you pass a DOM element into JSON.stringify then you get a (JSON representation of an) empty object out.
DOM elements don't have any properties that will be automatically processed by JSON.stringify.
If you want to get data from a form, then you need to actually read the data from the form (e.g. get the input elements and read their value properties).
If you want to extract data from the DOM of a Google Form or if you want to send a postMessage from a Google Form then you need to write the JavaScript which does that in the HTML document containing the form.
You can't pull it across domains.
The page with the Google Form has access to the data and can push it across domains with post message.
Nothing you can do will let you just help yourself to that data.
It's private between the owner of the browser and the owner of the website and one of them needs to take explicit action if it is to be shared with anyone else (e.g. someone who has wrapped that website in an iframe).

Detect from where (which website) the http request came from

I want to build my own analytic and I need to know from where the requests are coming from with only javaScrpt, I can't believe that the browser is not holding somewhere in window object a variable about from where the request came from. It looks like there is no information in the net or I am not asking the right question.
I hope somebody met this problem before and has a solution :) Thanks!
You can use document.referrer
Syntax
var referrer = document.referrer;
Value
The value is an empty string if the user navigated to the page directly (not through a link, but, for example, by using a bookmark). Because this property returns only a string, it doesn't give you document object model (DOM) access to the referring page.
Inside an <iframe>, the Document.referrer will initially be set to the same value as the href of the parent window's Window.location.

Accessing localStorage of an iFrame in an event callback?

I'm trying to cheat a bit with localStorage. The spec defines that when a value in localStorage changes, all other open pages on the same domain receive a storage event callback. I would also like the event to fire on the page where the value was changed.
I added a hidden iFrame to each page which loads an empty document from the same domain and tried using it as the target for the localStorage change (so technically the page that I'm looking at isn't the origin of the localStorage change)
It works fine except for when I do the same thing inside an event callback...
function fnSetupMusicPlayerSection(i, oSection) {
var oAudio, oLocalStorageFrame, oLocalStorageWindow;
oAudio = oSection.querySelector('audio');
oLocalStorageFrame = oSection.querySelector('iframe.local-storage-target');
oLocalStorageWindow = oLocalStorageFrame.contentWindow || oLocalStorageFrame;
oLocalStorageWindow.localStorage.setItem('loadSetter', '1111');
oAudio.addEventListener('play', function(oEvent) {
oLocalStorageWindow.localStorage.setItem('callbackSetter', '2222');
});
}
loadSetter is successfully stored and all windows receive the storage event. When I click to play the audio I get the following error inside the callback - Uncaught DOMException: Failed to execute 'setItem' on 'Storage': access is denied for this document.
Is there anything I can do to solve this? I really don't want to have to write code to update the current page separately
Update: I don't know if I'm doing something wrong in the example I gave above but the code does seem to work inside some callbacks. I have an anchor on the page with a click event where I can set localStorage through the iFrame
You can try postMessage API to enable communication between your page and iFrame. In brief, send a message to instruct iFrame to update its localStorage, and another message to ask iFrame to return its localStorage content whenever you need the data you sent.
Be careful since:
This is a HTML5 API. Check if your app's minimum requirements allows the implementation.
This is a cross-origin communication, which means if other pages in your browser use postMessage, your iFrame will receive it too. You might need to add info into message to notice iFrame which message it should read.

Passing values from child window to parent window which is on another domain?

Parent window is served from my.salesforce.com domain and the child (pop-up) window is served from another domain, visual.force.com. The functionality is to populate the value the user selects in pop-up back to the parent window. I was using the window.opener to communicate with the parent window, but I get the error message " Domains, protocols and ports must match" in the parent window.
Any idea how this could be avoided? and the values passed from child to the parent?
-Sameer
You might be able to hack around this with srcUp function. It's not an official API, blah blah blah but I seem to recall it's used by SF, especially around Service Cloud Console.
http://boards.developerforce.com/t5/Java-Development/Issue-with-javascript-button-within-Service-Console-need-advice/td-p/290171
http://boards.developerforce.com/t5/Visualforce-Development/Getting-quot-Not-Implemented-quot-Javascript-error-on-SrcUp/td-p/361585
https://salesforce.stackexchange.com/questions/5009/open-a-service-console-primary-tab-from-a-custom-component-module (pity the link from techtrekker's comment has expired).
http://salesforcedevbj.blogspot.com/2012/10/custom-links-and-buttons-in-service.html
Sorry, not a real answer, I never had to hack stuff like that... but at least you have some ray of hope to Google for now. Also try cross-posting on salesforce.stackexchange.com?
Javascript cannot communicate across domains for security reasons, as it breaks the Same Origin Policy.
Not sure it's possible, but you might want to look into seeing if you can make a JSON-P call to a webservice on the parent domain, which sets values on the server-side, which are then read by the parent page.
You can read about JSON-P here:
What is JSONP all about?
I suffered same problem. I think that if you are on salesforce standard edit page(Parent) then
create a visualforce edit page(assume page name is Test) same as this page. After that go to object for which you implementing this and edit 'edit' standard button and then you will get ovveride with option select visualforce page Test.
If i m getting wrong understanding of ur question then please let me know....

Passing objects from child to parent page

I have two web pages, parent and child. On click of add in parent I open popup and where i will enter the details and save. Upon save I would like refresh the data in parent Grid. Following are the approcahes i was thinking
Use Session varible on save and use it on parent.
Store the values in DB on child and retrive it.
Serilize the object in Child and assign that to partent hidden varible on refresh de-serilze on server and bind it to the grid.
I would like to know from the group what is the best way to do this. I was thinking of doing the third approcah?
I am using ASP.NET 4.0 and Jquery for popup window.
I would like to do it this way:
OnSave in child page wait for
server response and
if save is successful do
parent.grid.reload (this is very
generic but i hope you get the point)
if save is not successful you can
do some nice exception/error
handling on child page and ask again
for input etc
Best not to refresh the master. User will not expect that and may loose
some state on the master, for example filter/sorting.
Best use window to window communication with window.opener to directly tell master what detail was edited or inserted.
If you want a quick update to the parent window without reloading it, you can access/modify variables/functions in parent window's scope (provided they are on the same domain) with window.parent.somevar = 'x' or window.parent.someFoo(). But you would still need to make a server request from one of the windows in order to store it in DB.
In general (not specifically in asp.net) I would just have the popup send a "refresh from DB" signal in one way or another to its parent (with window.parent).
The advantage of this is that you make sure that the parent gets the actual data that was saved. Otherwise you would need the pop-up to check itself if the save succeeded, and if the object that it wants to send to its parent is actually correct.
Edit: Actually the pop-up should probably check for saving success as another answer says. Still I wouldn't have it do more than call a refresh method on the caller window, unless maybe that refresh is extremely costly.

Categories