Executing JavaScript in an Ajax Response
I don’t know how, in the many years that I have been building web sites and using Ajax, I have never encountered a situation where I needed to return mixed HTML and executable JavaScript in an Ajax response. I routinely return JSON (JavaScript Object Notation) in Ajax responses as well as plain HTML but never both. Recently that changed.
The Ajax call I was working on asynchronously adds a row to a database table and upons successful completion, updates the page in the browser then adds a new element to a selector, using the new unique row ID from the row that was added to the database table. Without thinking it through, and assuming I knew exactly how to do this, I just appended the JavaScript code to upated my selector in the Ajax responseText as a SCRIPT element and expected it to be executed the same as when a web page is rendered.
It didn’t work. But why? Simply stated, because the W3C Specification for XMLHttpRequest states:
"Scripts in the resulting document tree will not be executed, resources referenced will not be loaded and no associated XSLT will be applied."
http://www.w3.org/TR/XMLHttpRequest/#document-response-entity-body
The Solution The solution is to call JavaScript’s eval() function on the text of the script. Using jQuery it is very easy to iterate through the collection of script tags and to eval() contents of the TextNode.
This is the code in our main web page:
<button onclick="doJavaScriptInAjax();" >Test It</button>
<div id="response-div"></div>
<script type="text/javascript">
function doJavaScriptInAjax() {
$.ajax({
url: "/snippets/js-in-ajax-response.html",
context: document.body,
success: function(responseText) {
$("#response-div").html(responseText);
$("#response-div").find("script").each(function(i) {
eval($(this).text());
});
}
});
};
</script>
And this is the content of the Ajax response:
<p class="ajax-result info">
The contents of this message were loaded via Ajax.
A piece of JavaScript also included in the Ajax responseText
will change the background color of this message in a few seconds.
</p>
<script type="text/javascript">
var timerId = setTimeout(function() {
$("#response-div")
.find("p")
.removeClass("info")
.addClass("success");
}, 7000);
</script>
Test It
At first glance, this may look like a bad coding practice, but I am not doing anything that one would not typically do when loading a JSON response:
<script type="text/javascript">
function doAjax() {
$.ajax({
url: "some-json-data.js",
context: document.body,
success: function(responseText) {
var json = eval("(" + responseText + ")");
}
});
};
</script>