prevent script execution on load (or parsing) in jQuery - javascript

i have an "ajax engine" based on jQuery. And after receiving the response i must manipulate the response before inserting in page. But I need the response as an jQuery object.
I do this with:
tmpActualResult = $( '<span/>' ).html( actualResult );
But at this time jQuery (or browser itself?) execute the scripts which are included in response - and exactly this shouldn't occur. I will execute the scripts manual later.
in particular I will:
parse response (get access to the tags/elements which are included)
insert the elements into page (DOM manipulating)
execute possible scripts (which are received)
in thanks
Edit:
If someone is interested too, i've created a more general solution by extending the jQuery.domManip function, as suggested in first answer.
(function($, oldDomManip) {
// Override the core domManip function
$.fn.domManip = function() {
function evalScript(i, elem) {
jQuery.globalEval(elem.text || elem.textContent || elem.innerHTML || "");
}
var scripts = [];
// let jQuery build the fragments
var results = jQuery.buildFragment(arguments[0], this, scripts);
// change parameter to created fragments
arguments[0] = results.fragment.childNodes;
// do what jQuery will do - without scripts
oldDomManip.apply(this, arguments);
if (scripts.length) {
// if connection is active
if (jQuery.active != 0) {
$(this).bind("ajaxStop.scriptCache", function() {
$.each(scripts, evalScript);
$(this).unbind("ajaxStop.scriptCache");
});
} else
$.each(scripts, evalScript);
}
return this;
};
})(jQuery, jQuery.fn.domManip);
it will not execute scripts before ajax request is completed. worked for me.

You can't prevent script execution of scripts that are part of a string of markup you're trying to add using .html() or .append(). At least not without extending the domManip function of jQuery. It extracts all scripts in html markup that you're trying to add using .html() or .append() and evals it.
You could, however, extract the script tags with their inner text (js) from the string yourself, add only the markup via .html() or .append() and then eval the scripts yourself when you need to.

Try this:
var container = document.createElement('div');
container.innerHTML = "<div> i am html with script tag <script type="text/javascript">alert('Script executed');</script>end</div>"
$(container).doSomething()

Related

Using script tag to pass arguments to JavaScript

I need to implement a cross-site comet http server push mechanism using script tag long polling. (phew...) For this, I dynamically insert script tags into the DOM and the server sends back short js scripts that simply call a local callback function that processes the incoming messages. I am trying to figure out a way to associate each one of these callback calls with the script tag that sent it, to match incoming replies with their corresponding requests.
Clearly, I could simply include a request ID in the GET url, which is then returned back in the js script that the server generates, but this creates a bunch of unnecessary traffic and doesn't strike me as particularly elegant or clever.
What I would like to do is to somehow associate the request ID with the script tag that I generate and then read out this request ID from within the callback function that is called from inside this script tag. That way, all the request management would remain on the client.
This leads me to the following question: Is there a way to ask the browser for the DOM element of the currently executing script tag, so I can use the tag element to pass arguments to the contained javascript?
I found this thread:
Getting the currently executing, dynamically appended, script tag
Which is asking exactly this question, but the accepted answer isn't useful to me since it still requires bloat in the server-returned js script (setting marker-variables inside the script) and it relies on unique filenames for the scripts, which I don't have.
Also, this thread is related:
How may I reference the script tag that loaded the currently-executing script?
And, among other things, suggests to simply grab the last script in the DOM, as they are executed in order. But this seems to only work while the page is loading and not in a scenario where scripts are added dynamically and may complete loading in an order that is independent of their insertion.
Any thoughts?
PS: I am looking for a client-only solution, i.e. no request IDs or unique callback function names or other non-payload data that needs to get sent to and handled by the server. I would like for the server to (theoretically) be able to return two 100% identical scripts and the client still being able to associate them correctly.
I know you would like to avoid discussions about changing the approach, but that's really what you need to do.
First, each of the script tags being added to the DOM to fire off the poll request is disposable, i.e. each needs to be removed from the DOM as soon as its purpose has been served. Else you end up flooding your client DOM with hundreds or more dead script tags.
A good comparable example of how this works is jsonp implementations. You create a client-side named function, create your script tag to make the remote request, and pass the function name in the request. The response script wraps the json object in a function call with the name, which then executes the function on return and passes the json payload into your function. After execution, the client-side function is then deleted. jQuery does this by creating randomly generated names (they exist in the global context, which is really the only way this process works), and then deletes the callback function when its done.
In regards to long polling, its a very similar process. Inherently, there is no need for the response function call to know, nor care, about what script tag initiated it.
Lets look at an example script:
window.callback = function(obj){
console.log(obj);
}
setInterval(function(){
var remote = document.createElement('script');
remote.src = 'http://jsonip.com/callback';
remote.addEventListener('load', function(){
remote.parentNode.removeChild(remote);
},false);
document.querySelector('head').appendChild(remote);
}, 2000);​
This script keeps no references to the script elements because again, they are disposable. As soon as their jobs are done, they are summarily shot.
The example can be slightly modified to not use a setInterval, in which case you would replace setInterval with a named function and add logic into the remote load event to trigger the function when the load event completes. That way, the timing between script tag events depends on the response time of your server and is much closer to the actual long polling process.
You can extend this even further by using a queueing system to manage your callbacks. This could be useful if you have different functions to respond to different kinds of data coming back.
Alternatively, and probably better, is to have login in your callback function that handles the data returned from each poll and executes whatever other specific client-side logic at that point. This also means you only need 1 callback function and can get away from creating randomly generated callback names.
If you need more assistance with this, leave a comment with any specific questions and I can go into more detail.
It's most definitely possible but you need a little trick. It's a common technique known as JSONP.
In JavaScript:
var get_a_unique_name = (function () {
var counter = 0;
return function () {
counter += 1;
return "function_" + counter;
}
}()); // no magic, just a closure
var script = document.createElement("script");
var callback_name = get_a_unique_name();
script.src = "/request.php?id=12345&callback_name=" + callback_name;
// register the callback function globally
window[callback_name] = function (the_data) {
console.log(the_data);
handle_data(the_data); // implement this function
};
// add the script
document.head.appendChild(script);
The serverside you can have:
$callback_name = $_GET["callback_name"];
$the_data = handle_request($_GET["id"]); // implement handle_request
echo $callback_name . "(" . json_encode($the_data) . ");";
exit; // done
The script that is returened by /request.php?id=12345&callback_name=XXX will look something like this:
function_0({ "hello": "world", "foo" : "bar" });
There may be a solution using onload/onreadystate events on the script. I can pass these events a closure function that carries my request ID. Then, the callback function doesn't handle the server reply immediately but instead stores it in a global variable. The onload/onreadystate handler then picks up the last stored reply and tags it with the request ID it knows and then processes the reply.
For this to work, I need to be able to rely on the order of events. If onload is always executed right after the corresponding script tag finishes execution, this will work beautifully. But, if I have two tags loading simultaneously and they return at the same time and there is a chance that the browser will execute both and afterwards execute botth onload/onreadystate events, then I will loose one reply this way.
Does anyone have any insight on this?
.
Here's some code to demonstrate this:
function loadScript(url, requestID) {
var script = document.createElement('script');
script.setAttribute("src", url);
script.setAttribute("type", "text/javascript");
script.setAttribute("language", "javascript");
script.onerror = script.onload = function() {
script.onerror = script.onload = script.onreadystatechange = function () {}
document.body.removeChild(script);
completeRequest(requestID);
}
script.onreadystatechange = function () {
if (script.readyState == 'loaded' || script.readyState == 'complete') {
script.onerror = script.onload = script.onreadystatechange = function () {}
document.body.removeChild(script);
completeRequest(requestID);
}
}
document.body.appendChild(script);
}
var lastReply;
function myCallback(reply) {
lastReply = reply;
}
function completeRequest(requestID) {
processReply(requestID, lastReply);
}
function processReply(requestID, reply) {
// Do something
}
Now, the server simply returns scripts of the form
myCallback(message);
and doesn't need to worry at all about request IDs and such and can always use the same callback function.
The question is: If I have two scripts returning "simultaneously" is it possible that this leads to the following calling order:
myCallback(message1);
myCallback(message2);
completeRequest(requestID1);
completeRequest(requestID2);
If so, I would loose the actual reply to request 1 and wrongly associate the reply to request 2 with request 1.
It should be quite simple. There is only one script element for each server "connection", and it can easily be stored in a scoped, static variable.
function connect(nameOfCallback, eventCallback) {
var script;
window[nameOfCallback] = function() { // this is what the response invokes
reload();
eventCallback.call(null, arguments);
};
reload();
function reload() {
if (script && script.parentNode)
script.parentNode.removeChild(script);
script = document.createElement(script);
script.src = "…";
script.type = "text/javascript";
document.head.appendChild(script);
// you might use additional error handling, e.g. something like
// script.onerror = reload;
// but I guess you get the concept
}
}

Trying to load advertiser script tags asynchronously - not working

I've got some banner zones set up on advertisespace.com - I'm trying to load the script tags using jquery so they load after the page has loaded. However its not working. Here is my code to do this.
Here is the function I use to include the script tag:
function jsinclude(file, dom) {
if (document.createElement && document.getElementsByTagName) {
if(dom=='undefined')
var dom = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', file);
script.setAttribute('charset',"utf-8");
dom.appendChild(script);
} else {
alert('Your browser can\'t deal with the DOM standard. That means it\'s old. Go fix it!');
}
}
ANd here is how I am calling the function:
$(function(){
jsinclude('http://ads.advertisespace.com/somethingsomething.js', document.getElementById('location-of-banner-1'));
jsinclude('http://ads.advertisespace.com/somethingsomething.js', document.getElementById('location-of-banner-2'));
})
The result is that the script tag is inserted in the correct place but the banners doesn't show i.e the code in the script file referred to is never executed. How can I fix this.
You tagged your question with jquery so why not make use of its advantages.
The dom is not the head element, and doing dom == 'undefined' checks whether the variable dom is equal to the string 'undefined'. You must have set that explicitly and that's probably not the case. Anyway, there is one head element so it's not necessary to pass it to the function - the function can handle that itself.
Also, checking for DOM functions is not applicable these days. We live in 2011, all browsers have these functions included.
function jsinclude(file) {
var script = $("<script>", { type: 'text/javascript',
src: file,
charset: 'utf-8' });
$('head').append(script);
}

How to insert just a part of a full page loaded using $.ajax() while executing embedded scripts?

I'm using $.ajax() to load new pages on my site if certain conditions are met (a flash-based radio player being active). However, I'd prefer not to modify the server-side output for this case.
Now I need a way to embed both the response on my page (that's easily done using .replaceWith()) but also execute javascripts embedded on this page.
One thought I had was creating a dummy div like <div id="onload" data-onload="functionname" data-onload-args="json-for-the-function-args"> but maybe there's a better way that doesn't require changing my html code (i.e. a pure js/jquery solution).
Note that using $(elem).load() is not possible as it does not evaluate any scripts if only a fragment of the retrieved document is used:
// inject the contents of the document in, removing the scripts
// to avoid any 'Permission Denied' errors in IE
I don't know any details about this IE problem but of course whatever code you are going to suggest should not cause errors in recent IE versions (I don't care about IE6).
Something along the lines of:
$('container').html(text_from_ajax_request);
$('container script').each(function(){
var $this = $(this);
src = $this.attr('src');
src ? $.getScript(src) : eval($(this).text());
});
Actually I solved it using a kind of dirty solution that works fine though:
I surround the actual content parts with comments that are not used anywhere else in my server-side template.
Then I fetch the whole content with dataType: 'text' and use string functions to extract the interesting part (this is safe since I look for the first starting comment and the last ending comment, so the actual content cannot cause any problems even if it contains those comments for some reason).
After this I use .html() to update my element. The important thing is that I do not create a DOM element from the retrieved html code since that would break the script tags.
Have you tried using load()? http://api.jquery.com/load/
I believe it should parse scripts and execute them for you.
EDIT:
Ok, either the bit about load() not being usable as is wasn't in the question or I didn't spot it. With that in mind I created a new version of load without the script stripping and it seems to work find in IE6,7,8, Chrome and Firefox... not really sure why the jQuery library does that:
<script type="text/javascript">
$(function() {
setTimeout(function() {
$('#target').load2('inject.html #inject');
}, 5000);
});
jQuery.fn.extend({
load2: function(url, params, callback) {
if (typeof url !== "string" && _load) {
return _load.apply(this, arguments);
// Don't do a request if no elements are being requested
} else if (!this.length) {
return this;
}
var off = url.indexOf(" ");
if (off >= 0) {
var selector = url.slice(off, url.length);
url = url.slice(0, off);
}
// Default to a GET request
var type = "GET";
// If the second parameter was provided
if (params) {
// If it's a function
if (jQuery.isFunction(params)) {
// We assume that it's the callback
callback = params;
params = undefined;
// Otherwise, build a param string
} else if (typeof params === "object") {
params = jQuery.param(params, jQuery.ajaxSettings.traditional);
type = "POST";
}
}
var self = this;
// Request the remote document
jQuery.ajax({
url: url,
type: type,
dataType: "html",
data: params,
// Complete callback (responseText is used internally)
complete: function(jqXHR, status, responseText) {
// Store the response as specified by the jqXHR object
responseText = jqXHR.responseText;
// If successful, inject the HTML into all the matched elements
if (jqXHR.isResolved()) {
// #4825: Get the actual response in case
// a dataFilter is present in ajaxSettings
jqXHR.done(function(r) {
responseText = r;
});
// See if a selector was specified
self.html(selector ?
// Create a dummy div to hold the results
jQuery("<div>")
// inject the contents of the document in, removing the scripts
// to avoid any 'Permission Denied' errors in IE
.append(responseText/*.replace(rscript, "")*/)
// Locate the specified elements
.find(selector) :
// If not, just inject the full result
responseText);
}
if (callback) {
self.each(callback, [responseText, status, jqXHR]);
}
}
});
return this;
}
});
</script>

Running scripts in an ajax-loaded page fragment

My web app dynamically loads sections of its UI with jquery.ajax. The new UI sections come with script though. I'm loading them as such:
Use...
$.ajax({
url: url,
dataType: 'html',
success: function(data, textStatus, XMLHttpRequest) {
$(target_selector).html( data );
update_ui_after_load();
}
});
This almost works. The problem is that the scripts included in the dynamic part of the page run before the new page fragment is inserted into the DOM. But often these scripts want to modify the HTML they're being delivered with. My best hacky solution so far is just to delay the scripts some reasonable amount of time to let the DOM insertion happen, by wrapping them in a setTimeout:
window.setTimeout( function() {
// process downloaded Fragment
}, 300);
Obviously this is unreliable and hideous. What's a better way?
Using
$(function);
will make the function you pass to jQuery to be run after the fragment is inline on the page.
I found it in
ASP.NET Ajax partial postback and jQuery problem
after looking at your question.
Are you familiar with the live() function? Might be what you're looking for here.
http://api.jquery.com/live/
The problem is that the scripts included in the dynamic part of the page run before the new page fragment is inserted into the DOM. But often these scripts want to modify the HTML they're being delivered with.
I'm fairly sure that in that case, the only sensible thing is to place the script after the HTML element.
Everything else would become kludgy quickly - I guess you could implement your own "ready" handler that gets executed after your HTML has been inserted, but that would be a lot of work to implement for no real gain.
I solved it by making a new simple ready handler system as follows...
var ajaxOnLoad = (function() {
var ajaxOnLoad = {};
var onLoadQueue=[];
ajaxOnLoad.onLoad= function(fn) {
onLoadQueue.push(fn);
}
ajaxOnLoad.fireOnLoad = function() {
while( onLoadQueue.length > 0 ) {
var fn = onLoadQueue.shift();
fn();
}
}
window.ajaxOnLoad = ajaxOnLoad;
return ajaxOnLoad;
})();
So in the pages which get .ajax() loaded, the scripts are queued to run with
ajaxOnLoad.onLoad( function() {
// Stuff to do after the page fragment is inserted in the main DOM
});
and in the code which does the insertion, before the update_ui_after_load() call, run
ajaxOnLoad.fireOnLoad();
A more complete solution could parse the pages, find script tags, and queue them up automatically. But since I have complete control of the fragments being inserted, it's easier for me to switch to using ajaxOnLoad.onLoad.

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