Access server response from same javascript function that initiated the request - javascript

With complete control over both the client and server side code, I'd like to accomplish the following:
Initiate a server request in a javascript function
Be able to abandon the request (from the user experience perspective) after a specified time
Access information about the response (e.g. either a redirect URL or part of the response body) before exiting the original function (this part is non-negotiable; setting a window interval, for example, will not cut it)
This sounds a lot like multithreading to me, which of course javascript doesn't do. Perhaps there's no solution, but I'm exhausting my options before admitting to that. In the non-working example below, function foo() sets an iframe's src to the url of a page -- redirect.aspx here -- which after a short delay redirects to another page with some UUID in the query string. (Note: it could just as well return the UUID in a hidden field in the response body, or via some other strategy; I have control over this).
Regardless how the server page returns the result, my goal is to access the UUID from the server before foo() exits.
Update: Suggested Unit Test
Though this question appears to be about scope -- and therefore solvable via closures (test pending) -- it's actually about continuity of execution. A successful test would consist of:
Create the foo() function
Assign something.onClick = foo()
foo() somehow initiates a server call and retrieves a URL from the response
foo() then calls window.open(url); using that URL
A window opens in all major browsers (critical case: IE 7, 8 & 9)
I do not currently know of a strategy that can pass this test.
Non-working sample:
<iframe id="aFrame" src="" height="0" width="0"></iframe>
<script type="text/javascript" language="javascript">
function foo() {
var f = document.getElementById("aFrame");
var loc = "http://localhost:8080/redirect.aspx?after=1000";
f.src = loc;
var start = new Date().getTime();
while (elapsedSince(start < 5000)) { // allow for server response
// FAIL: this is never true until after foo() exits:
if (f.src != loc) {
alert(encodeURIComponent(f.src));
return true;
}
}
alert("Timeout");
return false;
}
function elapsedSince(startTime) { // omitted safety checks for brevity:
return new Date().getTime() - startTime;
}
</script>
I'm not an ace at Ajax functions, but according to my understanding they require a callback, which means any return information arrives outside of the initiating function. Fail.
The above strategy doesn't work, per comments in the js code.
Other strategies might include something like using document.createElement() to create the iframe and .insertBefore() to add it to the DOM, but I'm still not certain I would be able both to initiate that and access any response details from within the same iteration of foo().
Does anyone know of any strategy that meets the above criteria?

Here is some code that would pass your "test". It gets a URL from an external page and opens a new window with that URL.
It is non-blocking as you set up a listener for readystatechange events (callback).
function foo() {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/pathname', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
window.open(xhr.responseText);
}
};
};
button.onclick = foo;

Related

how to get function arg from external javascript file?

below code is in one javascript file
==> test.js:
function test(){ window.open('http://example.com/abc/c4adbeb687f77b48a041d70395803358','','toolbar=yes,statusbar=yes,location=yes,menubar=yes,scrollbars=yes,resizable=no,fullscreen=yes');}
how can i get http://example.com/abc/c4adbeb687f77b48a041d70395803358 from a html file with below structure
==>test.html:
<script type="text/javascript" src="test.js"></script>
<script>
alert(window.open.value);
</script>
If you have control over both the HTML and the JS, you can intercept the window.open function:
<script>
var oldWindowOpen = window.open;
var windowOpenArgs = [];
window.open = function() {
windowOpenArgs = arguments;
oldWindowOpen.call(window, arguments);
};
</script>
<script type="text/javascript" src="test.js"></script>
<script>
window.open = oldWindowOpen;
// do stuf with variable windowOpenArgs
console.log(windowOpenArgs[0]); //the url
</script>
You could get more advanced, by throwing and catching a error to find the stack trace and only record the parameters when the call comes from a specific function, but it all depends on the nature of your scripts:
window.open = function() {
try { throw new Error(); } except(e) {
var stack = e.stack;
if (/test#.*?test.js:/.test(stack)) {
windowOpenArgs = arguments;
}
}
}
However notice that the variable will only be set after test() is executed!
Your question seems a bit unclear.... to me.
I presume you are trying to rip the url that is located in a .js file. In your example you want the url http://example.com/abc/c4adbeb687f77b48a041d70395803358 to be ripped out of test.js.
Well, in your example, you have test.js loaded as its in the script tag as you showed here:
<script type="text/javascript" src="test.js"></script>
Therefore, if you know the name of the function that has the url data. You can simply:
alert(test.toSource());
to see its contents. However, it will need to be parsed. One could write a weird looking RegEx expression for it. But, here is a workaround:
alert("http"+test.toSource().split("http")[1].split("',")[0]);
This is a ROUGH workaround. Parsing heavily depends on how people write code. And, in this example, if they used a " instead of ' then it would not display the url correctly. There could be other issues, a nice RegEx could knock it out cleanly tho.
However, if you dont know the functions name. And you just want to blindly search the whole js file for a url (specific or in general), then I would suggest it best to load it with XMLHttpRequest. I guess there is a HttpRequest you could use as well, never looking into it.
var sUrl = "test.js";
var httpRequest = new XMLHttpRequest();
httpRequest.open("GET",sUrl, true);
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState == 4 && httpRequest.status == 200) { // ok
alert ( "http"+httpRequest.responseText.split("http")[1].split("',")[0] );
} else if ( httpRequest.readyState == 4 && httpRequest.status != 200 ) {
// failed
}
};
httpRequest.send(null);
That mess should show an alert box containing the first appearing http url in a function argument from any URL put into sUrl.
If you want more... then that means more parsing. But you can store it from here to do whatever.
But, once you open a window from javascript, you cannot access this new window's contents from within the opening javascript. Cross Domain violation
(some sort of extension of the Same Origin Policy). You can, however, change where its pointing. And, grab the location its pointed to, which might also be what your asking here.
In that case, if the window is already open you can do this
var w = window.open("http:\\example.com"); // some time before
alert(w.location);
However, if you just opened it. Like above. It might be about:blank, because it hasn't loaded yet. In which case you would have to look for it later.. like:
window.open("http:\\example.com", "MyName");
setTimeout('var w = window.open(,"MyName"); alert(w.location);', 1000);
Here, you have to use the window name parameter for window.open. Then, the second time you call window.open you leave the URL parameter blank and only use the window name parameter to get a reference to an existing open window... instead of opening a new one.
Then, if you want to ubiquitously grab all urls passed into window.open, there is a hijack method. It all just depends on what your asking here... but Kroltan beat me to it! lol! So check his answer for that one.
PT

Problems understanding/using callback function

In this discussion and in chat I understood that a callback is the only way to go!
"
Get from the server a link with ajax, write the link in a variable, open an xml with this link, doing some stuff with the xml: is callback the only way?
"
I'm trying to understand what a callback is. I read some blog, but I still have problems.
What I have now in JS is
1) a function to open an xml.
2) function to request the link of the xml in first function
Can someone provide an example in PLAIN JAVASCRIPT of how to nest these two functions?
The server generate the link of the xml because I'm making a multi user web site and every user has it's own xml. So I need to ask the server what is the link of the xml and then open it. Is there an easy way to achieve this? I need plain javascript no jquery.
Thanks!
In general, a "callback" is a function which will be executed at a later time when an asynchronous process is completed.
So you might start by defining the function that should happen when the data is retrieved from the server (the "second" function, intuitively, but you should define it first because it's the business functionality you're looking to achieve and not just an implementation concern). Something as simple as:
var doSomethingWithTheData = function () {
// do, well, something with the data
};
This assumes that you have the data, which you don't yet. But the AJAX call will get that data. You can now use this function as your callback for the AJAX call. Taking the AJAX example from MDN, you might have this:
var httpRequest;
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 8 and older
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
httpRequest.onreadystatechange = doSomethingWithTheData;
// perform the AJAX request
The httpRequest object will contain the response from the server after the AJAX call is executed and completed. (Remember that this happens asynchronously, so it won't contain the response on the immediate next line of code. It will at some later time which you don't control. Hence the need for the callback.)
I recommend walking through that full MDN article to get all the details, particularly on handling errors and such. But the data you're looking for (assuming nothing went wrong) would be in httpRequest.ResponseText. So, also assuming your variables are scoped to allow this (you can modify that as needed):
var doSomethingWithTheData = function () {
var data = httpRequest.ResponseText;
// do, well, something with the data
};
Excuse me, #David
var httpRequest;
if (window.XMLHttpRequest) { // Mozilla, Safari,
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 8 and older
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
var url = "http://myserver.mydomain/getMyUsersXMLUrl?user=pete";
httpRequest.open("GET", url, true); // next ajax to retrieve XML - File
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState == 4) { // response received
var response = httpRequest.responseText; // this should contain you url
httpRequest.open("GET", response, true); // next ajax to retrieve XML - File
// and the same as for the first request
}
}
If You just need to download a xml you dont need a callback . Just take a look at jquery and ajax. Retrieving a callback from a server is not possible . JSONP does deal with callbacks that are called by the server (actually they arent ) Butter you wont need it. I think you are messaging the xhttprequestobject
A callback function is simply a piece of executable code passed as a parameter to another piece of code. For example:
function first (arr, predicate) {
// no predicate supplied, return first element
if (!predicate) return arr[0];
for (var i = 0; i < arr.length; i++) {
// return first element satisfying predicate
if (predicate(arr[i])) return arr[i];
}
// no element satisfying predicate, return null
return null;
}
// second parameter is an anonymous function
// will alert 4, as it's the first element which will return true
alert(first([1, 2, 3, 4, 5, 6], function(n) { return n > 3; }));
Callbacks are useful for asynchronous tasks, or for library functions which need extra customization at runtime.

How to store variable within javascript to limit number of http calls?

I am using a second party file downloader which returns a progress event. I can capture the event and call a program on the server to perform an update (for security purposes so I can tell the most recent activity).
I get about 30 events per second all at percent downloaded 1%, then 30 more at 2%, then 30 more at 3%, etc. I would like to limit my http calls to only once per percentage change, 1%, 2%, 3%, etc. I would put a hidden field on the page and compare that and update it, but I cannot refresh the page since the download is in progress.
Is there a way to use some type of client side storage within javascript or jquery for this?
In other words, I need to be able to tell when the PercentCurrent value changes from 1 to 2, etc.
My javascript function looks like this:
function onProgress(PercentTotal, PercentCurrent, Index){
var xmlhttp;
//The handler will update the file progress
if (typeof XMLHttpRequest != 'undefined') {
xmlhttp = new XMLHttpRequest();
}
if (!xmlhttp) {
throw "Browser doesn't support XMLHttpRequest.";
}
var data = "";
xmlhttp.open("POST", "UpdateProgress.aspx?PercentCurrent=" + PercentCurrent, true);
//Send the proper header information along with the request
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//xmlhttp.setRequestHeader("Content-length", data.length);
xmlhttp.setRequestHeader("Connection", "close");
xmlhttp.send(data);
}
Thank you,
Jim
JavaScript does indeed have variables, you just need to store one in a scope that's accessible to your onProgress code. You may just be able to use a var in the same place onProgress is declared, but a simple and JavaScripty way to make that variable "private" is to use a closure:
var onProgress = (function(){
var lastSend = 0;
return function(PercentTotal, PercentCurrent, Index){
if (Math.floor(PercentCurrent) > lastSend) {
lastSend = PercentCurrent;
var xmlhttp…
}
}
})();
This'll look a little confusing if you haven't worked with JavaScript much. Here's what's going on:
I create a variable called onProgress
I create and immediately run an anonymous (unnamed) function, like this: (function(){ … })()
This function defines a local variable, lastSend, and returns the real onProgress function.
Whenever a function is called in JavaScript, it has access to the scope in which it was created. So, whenever onProgress() is called, it'll have access to the lastSend variable, and can check that progress is has moved past the next whole percent.
Of course, this is a bit ugly, and it can only be used once on a page (since there's only one closure with one lastSend variable. Instead of assigning it to a name, you might pass it directly into the function which calls it, anonymously (see below). Then, a new copy of the function, with a new closure, gets created when you hit downloadFile.
Your original question is tagged jquery. If you are indeed using jQuery on the page, you can simplify the posting of data significantly (down to one line) and make it more compatible, with jQuery.post:
$.post("UpdateProgress.aspx", { PercentCurrent: PercentCurrent });
(This would replace all the XMLHTTPRequest-related code in onProgress.)
So, using a closure and jQuery.post might look like this:
// Not sure what your second-party file downloader looks like
fileDownloader.downloadFile((function(){
var lastSend = 0;
return function(PercentTotal, PercentCurrent, Index){
if (Math.floor(PercentCurrent) > lastSend) {
lastSend = PercentCurrent;
$.post("UpdateProgress.aspx", { PercentCurrent: PercentCurrent });
}
}
})());
Have a look at jQuery's .data(). It allows you to store data and attach it to a particular DOM element like so:
$('body').data('foo', 52);
$('body').data('foo'); // 52
I am not sure to understand your problem. Is the page continously reloaded? If it is not all that you need to do is:
var lastPercent = null; // you need to initialize this when it all starts again.
function onProgress(PercentTotal, PercentCurrent, Index){
var xmlhttp;
if (lastPercent == PercentCurrent)
return; //Does nothing if no change occurred.
lastPercent = PercentCurrent;
//The handler will update the file progress
if (typeof XMLHttpRequest != 'undefined') {
xmlhttp = new XMLHttpRequest();
}
if (!xmlhttp) {
throw "Browser doesn't support XMLHttpRequest.";
}
var data = "";
xmlhttp.open("POST", "UpdateProgress.aspx?PercentCurrent=" + PercentCurrent, true);
//Send the proper header information along with the request
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//xmlhttp.setRequestHeader("Content-length", data.length);
xmlhttp.setRequestHeader("Connection", "close");
xmlhttp.send(data);
}

How to implement a getter-function (using callbacks)

I have to request data for a JS-script from a MySQL database (based upon a user-id).
I did not find a simple solution for JavaScript and it was not possible to load the data using ajax, because the database is available under a different domain.
I implemented a workaround using PHP and curl.
Now the JS has to "wait" for the request to finish, but the script is of course running asynchronously and does not wait for the response.
I know that it's not really possible to wait in JS, but it must be possible to return value like this.
I also tried using a return as another callback, but that didn't work of course, because the getter-function will run further anyway.
How can I implement a simple getter, which "waits" and returns the response from the HTTP-request?
Thanks for any other clues. I'm really lost at the moment.
This is a excerpt from the source code:
/**
* Simple getter which requests external data
*/
function simple_getter() {
// http request using a php script, because ajax won't work crossdomain
// this request takes some time. function finished before request is done.
/* Example */
var url = "http://example-url.com/get_data.php?uid=1234";
var response_callback = handle_result_response;
var value = send_request( url, response_callback );
value = value.split('*')[0];
if (value === '' || value == const_pref_none) {
return false;
}
/* 1. returns undefinied, because value is not yet set.
2. this as a callback makes no sense, because this function
will run asynchronous anyway. */
return value;
}
Additional information about the used functions:
/**
* Callback for the send_request function.
* basically returns only the responseText (string)
*/
function handle_result_response(req) {
// do something more, but basically:
return req.responseText;
}
/**
* Requests data from a database (different domain) via a PHP script
*/
function send_request( url, response_callback ) {
var req = createXMLHTTPObject();
if (!req)
return;
var method = (postData) ? "POST" : "GET";
req.open(method, url, true);
req.setRequestHeader('User-Agent','XMLHTTP/1.0');
// More not relevant source code
// ...
req.onreadystatechange = function () {
// More not relevant source code
// ...
response_callback(req);
}
if (req.readyState == 4)
return;
req.send(postData);
}
Not really relevant code, but required for the HTTP-request:
var XMLHttpFactories = [
function () {return new XMLHttpRequest()},
function () {return new ActiveXObject("Msxml2.XMLHTTP")},
function () {return new ActiveXObject("Msxml3.XMLHTTP")},
function () {return new ActiveXObject("Microsoft.XMLHTTP")}
];
function createXMLHTTPObject() {
var xmlhttp = false;
for (var i=0; i<XMLHttpFactories.length; i++) {
try {
xmlhttp = XMLHttpFactories[i]();
} catch (e) {
continue;
}
break;
}
return xmlhttp;
}
You really, really shouldn't try to synchronously wait for a network request to complete. The request may never complete, may hang and take a long time, and so on. Since JavaScript is single threaded, and in fact all major browser engines are single threaded, this will cause your entire page to hang while waiting for the request, and in some browsers, may cause the entire browser to hang.
What you should do is replace code like this:
var returned = some_request('http://example.com/query');
do_something_with(returned);
with code like this:
some_request('http://example.com/query', function (returned) {
do_something_with(returned);
});
That way, you will never cause your page or the browser to hang waiting for the request, and can simply do the work once the response comes in.
I don't see whats wrong with your code in general.
When you make a request, provide a Callback. When a response comes back, which you can easily detect, execute the Callback and pass it the result.
This is the way client side apps work.It is not procedural, but works by events.
You present the screen to the user and wait
The user makes an action
You call the server, set a callback and wait
The response come and you execute the callback and wait for another step 2
Rather than trying to change that, you need to fit with that or it will be a painful experience.
Javascript is not multithreaded. It means a single statement is run at a time. The real asynchronism come from the time the server takes to respond and call the callback. You never know which call will come first and need to build your program with that in mind.

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