views:

2452

answers:

8

I'm working on a web page where I'm making an AJAX call that returns a chunk of HTML like:

<div>
  <!-- some html -->
  <script type="text/javascript">
    /** some javascript */
  </script>
</div>

I'm inserting the whole thing into the DOM, but the JavaScript isn't being run. Is there a way to run it?

Some details: I can't control what's in the script block (so I can't change it to a function that could be called), I just need the whole block to be executed. I can't call eval on the response because the JavaScript is within a larger block of HTML. I could do some kind of regex to separate out the JavaScript and then call eval on it, but that's pretty yucky. Anyone know a better way?

+7  A: 

You don't have to use regex if you are using the response to fill a div or something. You can use getElementsByTagName.

div.innerHTML = response;
var scripts = div.getElementsByTagName('script');
for (var ix = 0; ix < scripts.length; ix++) {
    eval(scripts[ix].text);
}
Scott Nichols
A: 

I don't have the solution, but you may find what you're looking for here:

http://ajaxpatterns.org/On-Demand_Javascript

Chuck
A: 

The best method would probably be to identify and eval the contents of the script block directly via the DOM.

I would be careful though.. if you are implementing this to overcome a limitation of some off site call you are opening up a security hole.

Whatever you implement could be exploited for XSS.

Quintin Robinson
A: 

Old versions of prototype.js had this option, but it was obsoleted. Here are rescued pieces:

ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',

extractScripts: function() {
  var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
  var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
  return (this.match(matchAll) || []).map(function(scriptTag) {
    return (scriptTag.match(matchOne) || ['', ''])[1];
  });
},

evalScripts: function() {
  return this.extractScripts().map(function(script) { return eval(script) });
},

This would not compile by itself, but it's enough to work out the rest.

Or, simply find prototype.js 1.5 and use it.

squadette
+2  A: 

An alternative is to not just dump the return from the Ajax call into the DOM using InnerHTML.

You can insert each node dynamically, and then the script will run.

Otherwise, the browser just assumes you are inserting a text node, and ignores the scripts.

Using Eval is rather evil, because it requires another instance of the Javascript VM to be fired up and JIT the passed string.

FlySwat
+1  A: 

Script added by setting the innerHTML property of an element doesn't get executed. Try creating a new div, setting its innerHTML, then adding this new div to the DOM. For example:

<html>
<head>
<script type='text/javascript'>
function addScript()
{
    var str = "<script>alert('i am here');<\/script>";
    var newdiv = document.createElement('div');
    newdiv.innerHTML = str;
    document.getElementById('target').appendChild(newdiv);
}
</script>
</head>
<body>
<input type="button" value="add script" onclick="addScript()"/>
<div>hello world</div>
<div id="target"></div>
</body>
</html>
Ed
This script does not work in IE 7. IE 7 seems to have a bug with the appendChild function.
Eddie
A: 

You can use one of the popular Ajax libraries that do this for you natively. I like Prototype. You can just add evalScripts:true as part of your Ajax call and it happens automagically.

Diodeus
A: 

Hello, sorry to add to this discussion, I don't know when this was started (no year in dates). I am trying to use either Prototype or Ajax libraries or anything at all and I can't get the content to fire because it is not a script block but a call to an external js file, like this one:

I can find no way to make that work (to show the google ads block).

I can get scripts to work using Prototype, but not this kind of block.

Any ideas?

Thanks for ANY help. I am searching the world after a solution and I can't find one!