Call javascript from Iframe to aspx page [duplicate] - javascript

This question already has answers here:
Invoking JavaScript code in an iframe from the parent page
(17 answers)
Closed 5 years ago.
I have two pages a.aspx and b.aspx. a.aspx have an Iframe and I load the b.aspx page in that Iframe. Is it possible to call a javascript from b.aspx to a.aspx?
Thanks in advance

Let's say you have the following javascript function in b.aspx.
function DoSomething()
{
//Do you required tasks
}
And you want to call this function in a.aspx. a.aspx has b.aspx loaded in and iframe as follows.
<iframe id="fraExample" name="fraExample" scrolling="no" src="b.aspx"></iframe>
This is how you'll call function DoSomething written in b.aspx from a function written in a.aspx
function CallFunction()
{
// This function will be in a.aspx
// some tasks
var bFrame = document.getElementById('fraExample'); // Get the iframe
bFrame.contentWindow.DoSomething();
// some other tasks
}
This should work for you nice and easy!

One way to do this, which will also cover scenarios where you've got different origins for the outer page and the page in the IFrame (e.g. http://site1/a.aspx and http://site2/b.aspx) is to use the postMessage function.
This has the added advantage that you're "locking yourself in" to an API contract between a.aspx and b.aspx as, if you had a function called DoSomething in b.apx which was being called from a.aspx, if you decided to rename the function you'd need to make changes to both pages (and any other pages that hosted a.aspx in an IFrame). If you use the postMessage approach, the only place you'd need to change would be the listener for the "message" event in b.aspx.
If you put the following code in a.aspx:
function sendMessageToEveryIFrame(message)
{
/// <summary>Call a function against all frames</summary>
var frames = document.getElementsByTagName('iframe');
for (var i = 0; i < frames.length; i++)
{
try
{
var frame = frames[i];
frame.contentWindow.postMessage(message, "*");
}
catch (e)
{
// Code to handle errors would go here!
}
}
}
You could then call it (perhaps on a button click for testing purposes) by having:
sendMessageToEveryIFrame('Test message!');
You then need some code in b.aspx that reacts to messages coming in, for example:
// This wires up your function that processes the message
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
// logic that reacts to the event/message goes here
alert(event.data);
}
When you press the button in a.aspx in the browser (or whatever else you're using to trigger the call) b.aspx should pop up an alert window containing the text "Test message!".
Your receiveMessage function can then dispatch the request and effectively act as a broker. So, if you had two functions inside b.aspx that you wanted to call, you could re-jig the code like this:
In a.aspx:
function callFrameFunction(functionName, parameters)
{
/// <summary>Call a function against all frames</summary>
var frames = document.getElementsByTagName('iframe');
for (var i = 0; i < frames.length; i++)
{
try
{
var frame = frames[i];
var message =
{
Function: function,
Parameters : parameters
};
frame.contentWindow.postMessage(message, "*");
}
catch (e)
{
// Code to handle errors would go here!
}
}
}
This means that callFrameFunction takes two parameters, the name of a function and the parameters for the function. You'd then update the code in b.aspx so that it looks more like this:
// This wires up your function that processes the message
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
// logic that reacts to the event/message goes here
switch(event.data.FunctionName)
{
case "function 1":
alert('Function called was \'function 1\'');
break;
case "function 2":
alert('Function called was \'function 2\'');
break;
}
}
You can replace the calls to alert with calls to your functions, passing in the values in event.data.Parameters as appropriate.

Related

JavaScript: calling function of parent window from child window

I am opening a popup window from a page. The process goes like this:
var newwindow = window.open("mydomain.com/a.html", "Testing", 'height=600,width=800');
if (window.focus) { newwindow.focus() }
mydomain.com/a.html opens a popup mydomain.com/b.html
mydomain.com/b.html redirects the user to another site(say payment gateway)
paymentgateway.com/authenticate.html
paymentgateway.com/authenticate.html redirects the user to mydomain.com/success.html
From mydomain.com/success.html I want to execute a function written on mydomain.com/a.html
I have written
window.parent.LaunchFunction();
window.close();
but it's not working. What can be the issue? Is it possible to achieve?
I'm offering another way - use the local storage for communication between apps at the same origin (same protocol+domain+port).
You can add listener, that is fired when somebody change any property in localstorage.
First instance is listening
window.addEventListener('storage', onStorageChange, false);
function onStorageChange (e) {
if (e.key === 'messageText') {
var messageText = localStorage.getItem('messageText');
if (messageText) { // Prevent to call when fired by `removeItem()` below
console.log('New message from another instance has come:', messageText);
localStorage.removeItem('messageText'); // this will fires the `onStorageChange` again.
}
}
}
Second instance is sending the messages
localStorage.setItem('messageText', 'Hello from second app instance');

(cross-domain) Call parent function from outside iframe is forbidden

...but I do control part of the javascript inside the second domain (which integrates the iframe).
So, what I need is some workaround my problem. We have example2.com (this one holds the iframe) and example.com (this one is the original, within the iframe). Inside the iframe the user clicks a button that executes parent.redirectUser() and although I have that function defined in example2.com it fails to execute because it points the function as forbidden to access from within the iframe. Considering I can control the javascript in example2.com, is there any other way to workaround this situation? Thank you very much for your help...
Yes, you can do it.
You can use messages technique,
send message from child(example2.com):
parent.postMessage("Ok", "example.com");
in parent(example.com) you must add the following code:
//add eventlistener for message event
if (window.addEventListener) {
window.addEventListener("message", listener);
} else {
// IE8
window.attachEvent("onmessage", listener);
}
function listener(event) {
//check the message trust or not?
if (event.data == "Ok") {
//from parent you can call function, but function must be placed in global scope
redirectUser();
}
}

Run Javascript in iFrame context

I am attempting to run a script in a webpage, that should be executed in an <iframe>. Right now I can call a function that is set within the <iframe>.. I'm just having issues running a script to the <iframe>'s context.
Here's how I run a function set in the <iframe>
$('#iframe').get(0).contentWindow.performSearch();
Now instead of calling the preformSearch function, I wish to run a script - for this example, this is the script...
console.log('worked!');
My actual script is a big one, so I won't put it here - for the sake of this question.
So, is there any way to run that script through the <iframe>'s context? For example, my first guess would be/was..
$('#iframe').get(0).contentWindow.function(){
console.log('worked!');
}
I've never messed with running functions through something like an <iframe> before though, so I'm stumped.
Thanks for any help in advance!
NOTE I am using NW.js (Node-Webkit) to remove all <iframe> restrictions.
NOTE v2 The preformSearch() function was just a reforence on how I call functions in the frame.
You could try and use the messaging mechanizm
which means that on the parent frame you could send the message with
var win =$("#iframe").contentWindow;
win.postMessage({ messageType: "predefinedMessage", features: data }, '*');
and than in the iframe you could get the message
function FrameCommunicator(attachManager) {
var mfilesFrame;
function _actOnMessage(data) {
if (data.messageType === "predefinedMessage") {
//perform here what you need
}
}
window.addEventListener("message", function (e) {
var data = e.data;
mfilesFrame = e.source;
_actOnMessage(data);
}, false);
}
now the thing is that the iframe and the parent fram can have the same code if they reference to the remotely.
Another way would be just to send the content of the message as JS and run it with eval , but that is risky , bad practice and lotsa other things :)

Chrome extensions: best method for communicating between background page and a web site page script

What I want to do is to run go() function in image.js file. I've googled around and I understand that is not possible to run inline scripts.
What is the best method to call the JavaScript I want? Events? Messages? Requests? Any other way?
Here is my code so far:
background.js
chrome.browserAction.onClicked.addListener(function(tab) {
var viewTabUrl = chrome.extension.getURL('image.html');
var newURL = "image.html";
chrome.tabs.create({
url : newURL
});
var tabs = chrome.tabs.query({}, function(tabs) {
for (var i = 0; i < tabs.length; i++) {
var tab = tabs[i];
if (tab.url == viewTabUrl) {
//here i want to call go() function from image.js
}
}
});
});
image.html
<html>
<body>
<script src="js/image.js"></script>
</body>
</html>
image.js
function go(){
alert('working!');
}
There are various ways to achieve this. Based on what exactly you are trying to achieve (which is not clear by your question), one way might be better than the other.
An easy way, would be to inject a content script and communicate with it through Message Passing, but it is not possible to inject content scripts into a page with the chrome-extension:// scheme (despite what the docs say - there is an open issue for correcting the docs).
So, here is one possibility: Use window.postMessage
E.g.:
In background.js:
var viewTabURL = chrome.extension.getURL("image.html");
var win = window.open(viewTabURL); // <-- you need to open the tab like this
// in order to be able to use `postMessage()`
function requestToInvokeGo() {
win.postMessage("Go", viewTabURL);
}
image.js:
window.addEventListener("message", function(evt) {
if (location.href.indexOf(evt.origin) !== -1) {
/* OK, I know this guy */
if (evt.data === "Go") {
/* Master says: "Go" */
alert("Went !");
}
}
});
In general, the easiest method to communicate between the background page and extension views is via direct access to the respective window objects. That way you can invoke functions or access defined properties in the other page.
Obtaining the window object of the background page from another extension page is straightforward: use chrome.extension.getBackgroundPage(), or chrome.runtime.getBackgroundPage(callback) if it's an event page.
To obtain the window object of an extension page from the background page you have at least three options:
Loop through the results of chrome.extension.getViews({type:'tab'}) to find the page you want.
Open the page in the first place using window.open, which directly returns the window object.
Make code in the extension page call a function in the background page to register itself, passing its window object as a parameter. See for instance this answer.
Once you have a reference to the window object of your page, you can call its functions directly: win.go()
As a side note, in your case you are opening an extension view, and then immediately want to invoke a function in it without passing any information from the background page. The easiest way to achieve that would be to simply make the view run the function when it loads. You just need to add the following line to the end of your image.js script:
go();
Note also that the code in your example will probably fail to find your tab, because chrome.tabs.create is asynchronous and will return before your tab is created.

Calling a JavaScript function returned from an Ajax response

I have a system where I send an Ajax command, which returns a script block with a function in it. After this data is correctly inserted in the DIV, I want to be able to call this function to perform the required actions.
Is this possible?
I think to correctly interpret your question under this form: "OK, I'm already done with all the Ajax stuff; I just wish to know if the JavaScript function my Ajax callback inserted into the DIV is callable at any time from that moment on, that is, I do not want to call it contextually to the callback return".
OK, if you mean something like this the answer is yes, you can invoke your new code by that moment at any time during the page persistence within the browser, under the following conditions:
1) Your JavaScript code returned by Ajax callback must be syntactically OK;
2) Even if your function declaration is inserted into a <script> block within an existing <div> element, the browser won't know the new function exists, as the declaration code has never been executed. So, you must eval() your declaration code returned by the Ajax callback, in order to effectively declare your new function and have it available during the whole page lifetime.
Even if quite dummy, this code explains the idea:
<html>
<body>
<div id="div1">
</div>
<div id="div2">
<input type="button" value="Go!" onclick="go()" />
</div>
<script type="text/javascript">
var newsc = '<script id="sc1" type="text/javascript">function go() { alert("GO!") }<\/script>';
var e = document.getElementById('div1');
e.innerHTML = newsc;
eval(document.getElementById('sc1').innerHTML);
</script>
</body>
</html>
I didn't use Ajax, but the concept is the same (even if the example I chose sure isn't much smart :-)
Generally speaking, I do not question your solution design, i.e. whether it is more or less appropriate to externalize + generalize the function in a separate .js file and the like, but please take note that such a solution could raise further problems, especially if your Ajax invocations should repeat, i.e. if the context of the same function should change or in case the declared function persistence should be concerned, so maybe you should seriously consider to change your design to one of the suggested examples in this thread.
Finally, if I misunderstood your question, and you're talking about contextual invocation of the function when your Ajax callback returns, then my feeling is to suggest the Prototype approach described by krosenvold, as it is cross-browser, tested and fully functional, and this can give you a better roadmap for future implementations.
Note: eval() can be easily misused, let say that the request is intercepted by a third party and sends you not trusted code. Then with eval() you would be running this not trusted code. Refer here for the dangers of eval().
Inside the returned HTML/Ajax/JavaScript file, you will have a JavaScript tag. Give it an ID, like runscript. It's uncommon to add an id to these tags, but it's needed to reference it specifically.
<script type="text/javascript" id="runscript">
alert("running from main");
</script>
In the main window, then call the eval function by evaluating only that NEW block of JavaScript code (in this case, it's called runscript):
eval(document.getElementById("runscript").innerHTML);
And it works, at least in Internet Explorer 9 and Google Chrome.
It is fully possible, and there are even some fairly legitimate use cases for this. Using the Prototype framework it's done as follows.
new Ajax.Updater('items', '/items.url', {
parameters: { evalJS: true}
});
See documentation of the Ajax updater. The options are in the common options set. As usual, there are some caveats about where "this" points to, so read the fine print.
The JavaScript code will be evaluated upon load. If the content contains function myFunc(),
you could really just say myFunc() afterwards. Maybe as follows.
if (window["myFunc"])
myFunc()
This checks if the function exists. Maybe someone has a better cross-browser way of doing that which works in Internet Explorer 6.
That seems a rather weird design for your code - it generally makes more sense to have your functions called directly from a .js file, and then only retrieve data with the Ajax call.
However, I believe it should work by calling eval() on the response - provided it is syntactically correct JavaScript code.
With jQuery I would do it using getScript
Just remember if you create a function the way below through ajax...
function foo()
{
console.log('foo');
}
...and execute it via eval, you'll probably get a context problem.
Take this as your callback function:
function callback(result)
{
responseDiv = document.getElementById('responseDiv');
responseDiv.innerHTML = result;
scripts = responseDiv.getElementsByTagName('script');
eval(scripts[0]);
}
You'll be declaring a function inside a function, so this new function will be accessible only on that scope.
If you want to create a global function in this scenario, you could declare it this way:
window.foo = function ()
{
console.log('foo');
};
But, I also think you shouldn't be doing this...
Sorry for any mistake here...
I would like to add that there's an eval function in jQuery allowing you to eval the code globally which should get you rid of any contextual problems. The function is called globalEval() and it worked great for my purposes. Its documentation can be found here.
This is the example code provided by the jQuery API documentation:
function test()
{
jQuery.globalEval("var newVar = true;")
}
test();
// newVar === true
This function is extremely useful when it comes to loading external scripts dynamically which you apparently were trying to do.
A checklist for doing such a thing:
the returned Ajax response is eval(ed).
the functions are declared in form func_name = function() {...}
Better still, use frameworks which handles it like in Prototype. You have Ajax.updater.
PHP side code
Name of file class.sendCode.php
<?php
class sendCode{
function __construct($dateini,$datefin) {
echo $this->printCode($dateini,$datefin);
}
function printCode($dateini,$datefin){
$code =" alert ('code Coming from AJAX {$this->dateini} and {$this->datefin}');";
//Insert all the code you want to execute,
//only javascript or Jquery code , dont incluce <script> tags
return $code ;
}
}
new sendCode($_POST['dateini'],$_POST['datefin']);
Now from your Html page you must trigger the ajax function to send the data.
.... <script src="http://code.jquery.com/jquery-1.9.1.js"></script> ....
Date begin: <input type="text" id="startdate"><br>
Date end : <input type="text" id="enddate"><br>
<input type="button" value="validate'" onclick="triggerAjax()"/>
Now at our local script.js we will define the ajax
function triggerAjax() {
$.ajax({
type: "POST",
url: 'class.sendCode.php',
dataType: "HTML",
data : {
dateini : $('#startdate').val(),
datefin : $('#enddate').val()},
success: function(data){
$.globalEval(data);
// here is where the magic is made by executing the data that comes from
// the php class. That is our javascript code to be executed
}
});
}
This code work as well, instead eval the html i'm going to append the script to the head
function RunJS(objID) {
//alert(http_request.responseText);
var c="";
var ob = document.getElementById(objID).getElementsByTagName("script");
for (var i=0; i < ob.length - 1; i++) {
if (ob[i + 1].text != null)
c+=ob[i + 1].text;
}
var s = document.createElement("script");
s.type = "text/javascript";
s.text = c;
document.getElementsByTagName("head")[0].appendChild(s);
}
My usual ajax calling function:
function xhr_new(targetId, url, busyMsg, finishCB)
{
var xhr;
if(busyMsg !== undefined)
document.getElementById(targetId).innerHTML = busyMsg;
try { xhr = new ActiveXObject('Msxml2.XMLHTTP'); }
catch(e)
{
try { xhr = new ActiveXObject('Microsoft.XMLHTTP'); }
catch(e2)
{
try { xhr = new XMLHttpRequest(); }
catch(e3) { xhr = false; }
}
}
xhr.onreadystatechange = function()
{
if(xhr.readyState == 4)
{
if(xhr.status == 200)
{
var target = document.getElementById(targetId)
target.innerHTML = xhr.responseText;
var scriptElements = target.getElementsByTagName("script");
var i;
for(i = 0; i < scriptElements.length; i++)
eval(scriptElements[i].innerHTML);
if(finishCB !== undefined)
finishCB();
}
else
document.getElementById(targetId).innerHTML = 'Error code: ' + xhr.status;
}
};
xhr.open('GET', url, true);
xhr.send(null);
// return xhr;
}
Some explanation:
targetId is an (usually div) element ID where the ajax call result text will goes.
url is the ajax call url.
busyMsg will be the temporary text in the target element.
finishCB will be called when the ajax transaction finished successfully.
As you see in the xhr.onreadystatechange = function() {...} all of the <script> elements will be collected from the ajax response and will be run one by one. It appears to work very well for me. The two last parameter is optional.
I've tested this and it works. What's the problem? Just put the new function inside your javascript element and then call it. It will work.
This does not sound like a good idea.
You should abstract out the function to include in the rest of your JavaScript code from the data returned by Ajax methods.
For what it's worth, though, (and I don't understand why you're inserting a script block in a div?) even inline script methods written in a script block will be accessible.
I tried all the techniques offered here but finally the way that worked was simply to put the JavaScript function inside the page / file where it is supposed to happen and call it from the response part of the Ajax simply as a function:
...
}, function(data) {
afterOrder();
}
This Worked on the first attempt, so I decided to share.
I solved this today by putting my JavaScript at the bottom of the response HTML.
I had an AJAX request that returned a bunch of HTML that was displayed in an overlay. I needed to attach a click event to a button in the returned response HTML/overlay. On a normal page, I would wrap my JavaScript in a "window.onload" or "$(document).ready" so that it would attach the event handler to the DOM object after the DOM for the new overlay had been rendered, but because this was an AJAX response and not a new page load, that event never happened, the browser never executed my JavaScript, my event handler never got attached to the DOM element, and my new piece of functionality didn't work. Again, I solved my "executing JavaScript in an AJAX response problem" by not using "$(document).ready" in the head of the document, but by placing my JavaScript at the end of the document and having it run after the HTML/DOM had been rendered.
If your AJAX script takes more than a couple milliseconds to run, eval() will always run ahead and evaluate the empty response element before AJAX populates it with the script you're trying to execute.
Rather than mucking around with timing and eval(), here is a pretty simple workaround that should work in most situations and is probably a bit more secure. Using eval() is generally frowned upon because the characters being evaluated as code can easily be manipulated client-side.
Concept
Include your javascript function in the main page. Write it so that any dynamic elements can be accepted as arguments.
In your AJAX file, call the function by using an official DOM event (onclick, onfocus, onblur, onload, etc.) Depending on what other elements are in your response, you can get pretty clever about making it feel seamless. Pass your dynamic elements in as arguments.
When your response element gets populated and the event takes place, the function runs.
Example
In this example, I want to attach a dynamic autocomplete list from the jquery-ui library to an AJAX element AFTER the element has been added to the page. Easy, right?
start.php
<!DOCTYPE html>
<html>
<head>
<title>Demo</title>
<!-- these libraries are for the autocomplete() function -->
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/ui-lightness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<script type="text/javascript">
<!--
// this is the ajax call
function editDemoText(ElementID,initialValue) {
try { ajaxRequest = new XMLHttpRequest();
} catch (e) {
try { ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try { ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
return false;
}}}
ajaxRequest.onreadystatechange = function() {
if ( ajaxRequest.readyState == 4 ) {
var ajaxDisplay = document.getElementById('responseDiv');
ajaxDisplay.innerHTML = ajaxRequest.responseText;
}
}
var queryString = "?ElementID="+ElementID+"&initialValue="+initialValue;
ajaxRequest.open("GET", "ajaxRequest.php"+queryString, true);
ajaxRequest.send(null);
}
// this is the function we wanted to call in AJAX,
// but we put it here instead with an argument (ElementID)
function AttachAutocomplete(ElementID) {
// this list is static, but can easily be pulled in from
// a database using PHP. That would look something like this:
/*
* $list = "";
* $r = mysqli_query($mysqli_link, "SELECT element FROM table");
* while ( $row = mysqli_fetch_array($r) ) {
* $list .= "\".str_replace('"','\"',$row['element'])."\",";
* }
* $list = rtrim($list,",");
*/
var availableIDs = ["Demo1","Demo2","Demo3","Demo4"];
$("#"+ElementID).autocomplete({ source: availableIDs });
}
//-->
</script>
</head>
<body>
<!-- this is where the AJAX response sneaks in after DOM is loaded -->
<!-- we're using an onclick event to trigger the initial AJAX call -->
<div id="responseDiv">I am editable!</div>
</body>
</html>
ajaxRequest.php
<?php
// for this application, onfocus works well because we wouldn't really
// need the autocomplete populated until the user begins typing
echo "<input type=\"text\" id=\"".$_GET['ElementID']."\" onfocus=\"AttachAutocomplete('".$_GET['ElementID']."');\" value=\"".$_GET['initialValue']."\" />\n";
?>
I needed to get something to do this, I find that this has worked for a long time for me, just posting this here as one of many solutions, I like to have solutions without jQuery and the following function may help you, you can pass the full html with script tags in and it will parse and execute.
function parseScript(_source) {
var source = _source;
var scripts = new Array();
// Strip out tags
while(source.indexOf("<script") > -1 || source.indexOf("</script") > -1) {
var s = source.indexOf("<script");
var s_e = source.indexOf(">", s);
var e = source.indexOf("</script", s);
var e_e = source.indexOf(">", e);
// Add to scripts array
scripts.push(source.substring(s_e+1, e));
// Strip from source
source = source.substring(0, s) + source.substring(e_e+1);
}
// Loop through every script collected and eval it
for(var i=0; i<scripts.length; i++) {
try {
if (scripts[i] != '')
{
try { //IE
execScript(scripts[i]);
}
catch(ex) //Firefox
{
window.eval(scripts[i]);
}
}
}
catch(e) {
// do what you want here when a script fails
if (e instanceof SyntaxError) console.log (e.message+' - '+scripts[i]);
}
}
// Return the cleaned source
return source;
}
Federico Zancan's answer is correct but you don't have to give your script an ID and eval all your script. Just eval your function name and it can be called.
To achieve this in our project, we wrote a proxy function to call the function returned inside the Ajax response.
function FunctionProxy(functionName){
var func = eval(functionName);
func();
}

Categories

Resources