views:

248

answers:

3

I have some code where a <script type="text/javascript"> block is dynamically inserted.

This block contains a function, which has an <input type="button"> element above it (also dynamically inserted) call it with the onclick attribute.

However, it is not working, and Firebug says that the function is not defined when I attempt to click the button.

Is this expectable, and if so is there a workaround?

A: 

If you're using something like

document.getElementById('foo').innerHTML = blah

then no, it won't work.

An easy workaround is to use jQuery to do your insertion, which will execute the script in that block. Just use the html function on an element, passing it your HTML/script block.

ShZ
Is it just me that doesn't particularly appreciate getting answers like "use xxx library"? What if he's only writing 5 lines of code, is it really worth the browser downloading and parsing a library to perform a simple function?
Andy E
I actually am a jQuery user, however for this specific instance I do not want to use a library
a2h
A rabid jQuery fan at that!
a2h
As am I, it's just that there are a number of scenarios where cross-browser compatibility is not needed (intranets, desktop widgets, etc) and whilst libraries can still make things easier they might sometimes be overkill. I do wonder if some people know javascript outside of libraries...
Andy E
AndyE, I actually agree with you here - having someone tell you "use a library" often isn't very helpful. However in this particular answer I was speaking purely from experience, and jQuery was what I used to solve the problem; I wasn't aware that the DOM based alternative worked, to be perfectly honest. At the time there was no answer, so I figured one using jQuery was better than none at all.
ShZ
Don't mind me, it was 2am and I had my grumpy head on :-) I remember, you got your answer out while I was still writing mine and I agree, an answer is an answer at the end of the day and jQuery is always a suitable option for most devs.
Andy E
A: 

Dynamically inserted client-side or server-side?

Server-side it'll work - assuming that the server-side call isn't an AJAX postback (Cause then in reality the actual dynamic insertion is still client-side).

Client-side it won't (you can't simply inject java-script into a page after it's already loaded and expect it to execute). You'll have to actually explicitly execute the JavaScript in that case or use a framework like jQuery that'll do that for you.

Tim Schneider
+2  A: 

If you're writing it to an innerHTML property, it won't work except for in IE when the DEFER attribute is present:

scriptParentNode.innerHTML = '<span/><script defer="defer" type="text/javascript">blah();</script>';

Your options are, a) use the DOM to create the element and append it:

var head = document.getElementsByTagName("head")[0];
var sTag = document.createElement("script");
sTag.type = "text/javascript";
sTag.text = "blah();";
head.appendChild(sTag);

or b) use document.write at the time your HTML is parsed (but not after!)

<head>
  <script type="text/javascript">
    document.write('<script type="text/javascript">blah();</script>');
  </script>
</head>

EDIT

Seeing as I was downvoted apparently for the information regarding the defer attribute being incorrect, I feel it necessary to post a working example from the MSDN documentation. Whilst it is true that IE removes NoScope elements from the beginning of an innerHTML string, it's possible to work around this by adding a scoped element to the beginning of the HTML string (example updated above):

<HTML>
<SCRIPT>
function insertScript(){
    var sHTML="<input type=button onclick=" + "go2()" + " value='Click Me'><BR>";
    var sScript="<SCRIPT DEFER>";
    sScript = sScript + "function go2(){ alert('Hello from inserted script.') }";
    sScript = sScript + "</SCRIPT" + ">";
    ScriptDiv.innerHTML = sHTML + sScript;
}    
</SCRIPT>
<BODY onload="insertScript();">
    <DIV ID="ScriptDiv"></DIV>
</BODY>
</HTML>

Feel free to actually click the "Show me" button in the MSDN documentation for a working example. [link]

Andy E
I'm using the DOM method. Thanks.
a2h