in JavaScript

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

[snippet file=”js-in-ajax-snippet.html”]

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>

Leave a Reply

24 Comments

  1. I am trying this technique but finding that jquery can’t

    find the script tags. Even if you just look at the ajaxed

    content:

        alert(ajaxedContent);

    …the elements are completely omitted

    Doing an ajax find won’t locate them either…

       $(newElement).find(‘script’)   <- comes up empty

    I am using the jquery $.get() ajax shortcut, rather than $.ajax(), but I'd be surprised if that made any difference.

    Did you ever run into this?

  2. Hi

    Thank you for that nice peace of work. It works with one div container that has to be updated but when I tried to update two div container from one AJAX call, it doesn’t work. Any suggestions?

    ‘success’ => ” function( response ) {
                                $(‘#div_1’).html($(‘#div_1_ajax’, response).html());
                                $(‘#div_2’).html($(‘#div_2_ajax’, response).html());
                                $(‘#div_2’).find(‘script’).each(function(i) {
                                        eval($(this).text());
                                    });
                                },

    • Hey, my pleasure, Michael.

      It looks like you’re passing multiple chunks of HTML in a single Ajax call. Is that correct? That’s a clever way to cut down on Ajax calls.

      The only thing I can think of is that the code that searches for the script tags in #div_2 might be firing before the HTML from #div_2_ajax has fully rendered.

      If the HTML from #div_2_ajax is very large (i.e., has a lot of nodes), it could be taking it a while to render because jQuery has to first convert the strings to nodes, the add them to the DOM.

      I wonder if you can do something like this:

      ‘success’ => ” function( response ) {
      $(‘#div_1’).html($(‘#div_1_ajax’, response).html());
      $(‘#div_2’).html($(‘#div_2_ajax’, response).html());
      $(‘#div_2’).ready(function() {
      $(‘#div_2’).find(‘script’).each(function(i) {
      eval($(this).text());
      });
      });
      },

      I have no idea if that works but it might be worth a try.

      Is there any reason you can’t eval the scripts in “response” before pulling out the #div_1_ajax and #div_2_ajax chunks?

      • Thank you again, for answering so quick. You are right, with a single AJAX call was I going to update two div container, because both had new data. The reason why I use AJAX is a div container with a chat that I wanted to prevent from disconnecting/connecting with every reload when the user clicks somewhere on the page. I tried your proposed solution from the comment which unfortunately didn’t work. I can eval the scripts in the respone before pulling them out as you further proposed and I tried so, but it was still not working.

        My workaround for now is to just update one div container per AJAX call and to change the website that it fits into that container (excluding the chat). That works now with your solution from your site here. You saved me a lot of time.

        • If you are still interested in tinkering with this problem, it just occurred to me that you might try loading the Ajax response as XML. I think the default is to load it as a string. You should be able to set an option in the  jQuery Ajax call to return it as XML.

    • The second example with the link to a script won’t eval because it’s not JavaScript, it’s an HTML tag that loads a JavaScript link. If you want to load the script, you need to do something like this:

      function doAjax() {
      $.ajax({
      url: “some-json-data.js”,
      context: document.body,
      success: function(responseText) {
      var json = eval(“(” + responseText + “)”);
      $(responseText).find(“script”).each(function(i) {
      var src = $(this).attr(“src”);
      if (src != “undefined” && src != “”) {
      var e = document.createElement(‘script’);
      e.setAttribute(‘type’,’text/javascript’);
      e.setAttribute(‘charset’,’UTF-8′);
      e.setAttribute(‘src’, src + ‘?r=’+Math.random()*99999999);
      document.body.appendChild(e)
      }
      });
      }
      });
      };

  3. Hi Scott,

    Thanks for the useful thread.
    Everything looks good except the fact that my issue is with Methods loaded from eval.

    Upon an Ajax call, I am loading the HTML and changing the page on the fly.
    Issue is that the new HTML has some javascript that I have to load.

    Ideally I want a javascript method to be loaded in the browser. I am calling eval method to get the javascript method also. But unfortunately when I call this method by an onclick, it throws error that the Method is not defined.

    Can you help me with this.

    Thanks in advance

    • Is the function defined in the text of the SCRIPT block or an external file referenced in the Ajax? How you execute the JavaScript depends on whether it’s inline script or an external file.

  4. Hi Scott,

    Thanks for the wonderful article, well i would like to share my issue for the same.

    I created a simple html file, where i load a scripts.js file and execute a slideshow on the page. Same thing i am implementing dynamically.

    I have my div that gets loaded on ajax call, at the same time i have a script.js file which i want it to be loaded, but the file is not getting called and so is the functions in the file not getting executed, due to which my div breaks.

    Please provide me a solution wherein i can load the js file on ajax and use its function.

    Regards,
    Shraddha

    • Hey Shraddha. Loading external files is a different case because the
      SCRIPT tag is a special case. It’s HTML so it will be added to the DOM,
      but the script won’t be loaded. You’ll still loops through the Ajax
      response
      and identifies any SCRIPT tags, but rather than eval the text content
      of tag, you’ll need to load the script referenced in the SRC attribute.

      Take a look at http://api.jquery.com/jQuery.getScript/ to see how it’s done.

  5. Thank you very much Scott Lewis.
    It really solved my long years back problem…..i just used $(“#response-div”).find(“script”).each(function(i) {
    eval($(this).text());
    });
    from your code..

    Thank you again…

  6. Thank you very much Scott Lewis.
    It really solved my long years back problem…..i just used $(“#response-div”).find(“script”).each(function(i) {
    eval($(this).text());
    });
    from your code..

    Thank you again….

  7. Thanks for your article, it’s very useful for me.
    I want to ask you: how do response disqus’s javascript to html in view page source as this page?
    Regards,
    anhcuong91

  8. Scott, thanks so much for sharing this. You just made my day! I was messing around with it for 2 days now until I found this article. Now everything works fine on my page! 🙂

    Regards,
    Stefan

  9. Well. With jQuery you don’t have to “eval()” your response. jQuery does that to you. 😐
    Just remove the “eval()” line and see.

    • Hey, this is a pretty old post and I’m fairly certain that eval() was necessary at the time this was written, but thanks for the feedback. I’m sure it will help readers who come across this article in the future. Cheers.

Webmentions

  • Executing Ruby .erb view from Jquery: The span created by my javascript goes for a walk in the DOM - How-To Video October 8, 2015

    […] technify.me/user-experience/ja… […]