2

My site uses pushState to load pages. I have one issue, I want to use javascript on one of the pages but can't because it loads everything with AJAX. So what do I do? I've been told something about "parseScript" but I can't find enough information on it.

--Example--

I load using AJAX On my page I have this script:

<script type="text/javascript">
        function go(){
            alert('1');
        }
    </script>
<a href="javascript:void();" onClick="go();">GO!!!</a>

Nothing happens.

--Edit--

If I open up Google Chrome's debugger: "Uncaught ReferenceError: go is not defined" And the <script> tag is no where to be found

Jake
  • 1,469
  • 4
  • 19
  • 40

3 Answers3

2

Browsers don't seem to parse <script> element content that's added to the document via targetElement.innerHTML. That's probably what you're running into.

The best solution is to use a well-tested framework like jQuery for solving problems like this. They've already figured out how to safely and correctly inject scripts into the DOM. There's no sense re-inventing the wheel unless you absolutely can't spare the bandwidth for the library.


One way you might fix this is by separating the JavaScript from the HTML in the Ajax response, either by issuing two requests (probably slower) or by structuring your JavaScript and HTML within a JSON object (probably harder to maintain).

Here's an example:

<script>

function load_content(){
  var req = new XMLHttpRequest();
  req.open("GET", "ajax.json", true);
  req.onreadystatechange = function (e){
    if (req.readyState === 4){
      if (req.status === 200){

        // these three lines inject your JavaScript and
        // HTML content into the DOM
        var json = JSON.parse(req.responseText);
        document.getElementById("target").innerHTML = json.html;
        eval(json.js);
      } else {
        console.log("Error", req.statusText);
      }
    }
  };
  req.send(null);
}

</script>

<a href="#" onclick="load_content()">Load more stuff</a>
<div id="target"></div>

The document ajax.json on the server looks like this:

{
  "js": "window.bar = function (){ console.log(\"bar\"); return false; }",
  "html": "<p><a href=\"#\" onclick=\"bar();\">Log a message</a></p>"
}

If you choose this route, you must either:

  • namespace your functions: MyApp.foo = function (){ ... };, or
  • explicitly add your functions to the global namespace: window.foo = function (){ ... };.

This is because eval executes in the current scope, so your function definitions inherit that scope and won't be globally available. In my example, I chose the latter option since it's just a trivial example, but you should be aware of why this is necessary.

Please make sure to read When is JavaScript's eval() not evil? if you decide to implement this yourself.

Community
  • 1
  • 1
Brandan
  • 14,735
  • 3
  • 56
  • 71
  • One should NEVER use eval(). It is a security nightmare in the waiting. Instead, architect the system such that a client side function processes the json result. The fact is that json is a lightweight data transport language. It is not JavaScript, nor intended to be used as such. – Mad Man Moon Feb 11 '12 at 18:38
  • "Never" is a bit drastic. Both jQuery and Prototype rely on `eval` at some level for exactly this purpose. Is there another way to inject scripts into the DOM? Or are you referring specifically to my use of `eval` for parsing the string of JSON into a native Object? – Brandan Feb 11 '12 at 18:52
  • Definitely not for parsing json into a function. Again, it's bad practice. Frankly, I have *never* had to use it and I work on some pretty complex stuff. – Mad Man Moon Feb 11 '12 at 18:57
  • There are definitely other options to adding scripts to the dom. From appending a script element with a src reference, to using jsonp, to using the dreaded document.write (with the known tricks for writing the opening and closing script tags. You can even use server side code to generate the js output so long as its content type is returned as application/javascript or text/javascript. – Mad Man Moon Feb 11 '12 at 19:02
  • @MadManMoon I updated my example to use `JSON.parse` and to include some other caveats about `eval` and rolling your own script injection. – Brandan Feb 11 '12 at 22:12
  • you and I will have to agree to disagree on this one. In my opinion -- as well as that of many well known and respected software engineers within our industry -- that it is simply poor programming practice and unnecessary to use eval(). I look at your example and ask myself, "why is he including inline js in the on click event? Wouldn't it be better to follow a unintrusive js pattern? Why bother including the script code in json instead included in the it in the calling script within a callback?" – Mad Man Moon Feb 12 '12 at 01:44
  • @MadManMoon Rather than continuing this comment thread, please post an opposing answer to the original question. I felt like a discussion of obtrusive vs. unobtrusive JavaScript was far outside the scope of this question, so my example followed his. It also sounded like he needed to load a dynamically created script **and** HTML content from the server, not fire a callback on the client, so he could either issue separate requests (which I also suggested) or return a single structured response. Again, please post an answer rather than responding to this comment. – Brandan Feb 12 '12 at 14:37
0

I think it would be helpful to have a little more detail as to how the Ajax call is made and the content is loaded. That said, a few things of note:

  • the syntax for javascript:void() is invalid. It should be javascript:void(0). For that matter, using javascript:void() on the href of an anchor tag is generally bad practice. Some browsers do not support it. If you must use an tag, set the href to # and add "return false;" to the click event.
  • you should use a button tag instead of the a tag in this case anyway.
  • given what you have provided, it should work (aside from the syntax error with void())
Mad Man Moon
  • 741
  • 4
  • 10
0

If I were to do this I would use jquery's load call. That takes care of putting an ajax call ,and parsing tags for script/no-script elements.

IF you dont wanna use jquery, I would suggest you go online and find what the jquery load method does and implement the same as an event handler for your ajax call.

Neeraj
  • 8,408
  • 8
  • 41
  • 69