0

I have a webpage that makes an AJAX request to a PHP script. That PHP script responds with a valid JSON object, and sets the Content-type header to application/json.

The JSON format is as follows (as reported by console.log(JSON.stringify(data)) where data is the JSON response):

{
    "content": "<script type=\"text/javascript\">console.log(\"test\");</script>"
}

I then create a div with the class of "content" (remember data is my AJAX responseText JSON object):

var content = document.createElement("div");
content.setAttribute("class", "content");
content.innerHTML = data.content;

Finally, I append the content div to an existing div on my site.

existingDiv.appendChild(content);

I examine the source in the elements tab of Google Chrome's developer tools because it does a good job of showing what is an actual DOM node, and what is just text. The script block shows up as a DOM node.

Unfortunately, the script is not executed - console.log("test"); does not output test to the developer tools console.

The only option I've seen so far is to use eval() which I'm trying to avoid like the plague. Also, my content might contain more than just a script block. It could contain other HTML markup as well.

What am I missing? How can I get this dynamically added block of Javascript to execute?

A jQuery solution is acceptable.

6
  • 1
    Injecting a script tag based on an arbitrary string is exactly the same as using eval(), at least regarding the reasons why eval() should be avoided. Commented Feb 8, 2013 at 14:07
  • @MCL could you expand more on that in an answer? Commented Feb 8, 2013 at 14:17
  • 1
    I could answer that by asking you why you want to avoid eval() "like the plague" and then telling you that injecting arbitrary <script> tags carries the same risks. Basically, you are doing the same thing when using one of both approaches: You take arbitrary strings from some place on the internet and execute them trustingly. I assume you already know the dangers of using eval() Commented Feb 8, 2013 at 14:26
  • The location of the script is on the same domain, and its content can be trusted. My main concerns were with eval being perceived as "evil" and with performance as I've heard it is very slow. Your point is a good one though, and well taken. Commented Feb 8, 2013 at 14:29
  • 1
    If performance is your only concern, then script injection is the better choice. I wrote a quick fiddle and I must admit, I am a bit suprised. Although both approaches use some kind of "hackish afterwards" code evaluation, the script injection runs remarkably faster. Check it out here. Commented Feb 8, 2013 at 14:49

3 Answers 3

1

A possible solution is to use new Function() as:

$(document).ready(function(){
   var script = "window.alert('hi')";
   new Function(script)();
});

As to why your script is not executing, is what @Pointy suggested (but not entirely + a way to circumvent that), as shown by this code:

<script type='text/javascript'>
$(document).ready(function()
{
    $("<script type='text/javascript'>window.alert('hi');<\/script>").appendTo(document.body); // this will work

    var container = document.createElement("div"); // this won't work
    container.innerHTML = "<script type='text/javascript'>window.alert('hi');<\/script>";
    document.body.appendChild(container);

    var script = document.createElement("script"); // this will
    script.innerHTML = "window.alert('hi');";
    document.body.appendChild(script);
});
</script>

EDIT: added a requested unit test: http://jsperf.com/eithed-hvsa

Sign up to request clarification or add additional context in comments.

10 Comments

That would require me to change the value of data.content. Also, this wouldn't handle data.content containing regular HTML markup, as well as Javascript.
That is indeed correct - however, you can either use $.append - as $("<script type='text/javascript'>window.alert('hi');<\/script>here is some html<div>aaa</div>").appendTo(document.body); will still work, or use a Regexp to pull the contents of script from within the tags.
After adding the <div> to the DOM first, then using jQuery's .html(), the script is executed, as displayed in Pointy's updated answer.
Yup, as it does what I've suggested :D
Seeing they operate on different principles... But, to the test unit! jsperf.com/eithed-hvsa
|
1

use $.getScript() simple, cachable, straightforward :

http://api.jquery.com/jQuery.getScript/

(obviously you'll have to amend your server script that generates that json containing jscode & change headers)

2 Comments

What if my content also contains HTML markup: {"content" : "<h1>Hello</h1><script type="text/javascript">console.log("test");</script>"} for example.
well then send back a link to the script to get; but that's 2 request to the server instead of one though... or preload the script on page load (is it that big that it has to be loaded separately ?)
1

Setting .innerHTML does not evaluate <script> tags; it's just not what browsers do.

If you need to return JavaScript to be executed, you're better off just sending it as a string (without <script> tags) so that you can easily pass it to eval().

edit If you do this with jQuery instead of directly setting innerHTML (that is, if you use $(something).html()), and you add the content directly to something in the DOM (that is, not just an element that's been instantiated but not appended to the DOM), then jQuery will actively find and extract your <script> content and evaluate it.

7 Comments

Well it was invalid as you posted it; all anybody can do here is respond to what you post.
Your code is: content.setAttribute("class", content); - can you explain what that does, if not set the "class" attribute to be a reference to the element itself?
@crush maybe because you posted a question on Stackoverflow asking people to diagnose problems with your code.
@crush dude you need to think hard about how you want to act towards people trying to help you for free.
@crush check my update - I think jQuery can help directly, as it strips <script> tags and hands the contents to eval(). Here is a jsbin example showing .html() at work.. Like I said, I think you need to put the content directly in the DOM, not just in an element you've created.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.