Inline Script inside an ASP.NET AJAX UpdatePanel
When you wrap content with an UpdatePanel, it pretty much takes care of everything for you. But it can't do absolutely everything...
Take for example some inline script:
<script type="text/javascript">
alert('hi');
</script>
<p>Some html after the script</p>
Inline meaning it appears inline with the rest of your HTML.
First of all -- I'd say it's best not to use inline script if you don't have to. If you can move that script block to the bottom or the top of the page, or to a separate javascript reference, without consequence, then why not keep the script and UI nice and separate? But there are times when inline script is either necessary or just preferred. For example -- check out how you instantiate an instance of Silverlight on your page. That's inline script. It makes sense to keep the script where it is, since that's where you want your Silverlight app to be.
Then, along came UpdatePanel...
The over simplified way of explaining how UpdatePanel does its work on the client is through the innerHTML DOM property. When a delta is retrieved from the server, it finds itself in the existing DOM, disposes of the contents, and then assigns the new content via innerHTML. Thanks to a basically consistent behavior across the major browsers, the new content appears just as if it were there to begin with.
But inline script doesn't work this way. Setting the innerHTML of a DOM element to HTML which contains a script block does not cause that script to execute. The only way to execute arbitrary script dynamically is to eval() it directly, or dynamically create a script element with document.createElement("script") and inject it into the DOM.
So if we had the above HTML+SCRIPT inside an UpdatePanel, whenever it updated, the inline script would simply be ignored.
UpdatePanel realizes that there may be script to be executed. But it only knows about such scripts if they are registered through the ScriptManager's Register script APIs. If you use that API correctly, UpdatePanel once again picks up the slack and takes care of the rest for you automatically.
ScriptManager.RegisterStartupScript(this, typeof(Page), UniqueID, "alert('hi')", true);
UpdatePanel intercepts registrations like these during asynchronous postbacks and sends the content down to the client separately from the HTML. And than the client side PageRequestManager dynamically injects a script element for you. So one solution to this inline script problem is simply not to use inline script. If you use this Register API all the time, it will work whether there is an update panel involved or not.
But I want my Inline Script back!
Alright already. So here is a novel little control that gives you the benefits of inline script without having the draw back of not working in an UpdatePanel.
public class InlineScript : Control {
protected override void Render(HtmlTextWriter writer) {
ScriptManager sm = ScriptManager.GetCurrent(Page);
if (sm.IsInAsyncPostBack) {
StringBuilder sb = new StringBuilder();
base.Render(new HtmlTextWriter(new StringWriter(sb)));
string script = sb.ToString();
ScriptManager.RegisterStartupScript(this, typeof(InlineScript), UniqueID, script, false);
}
else {
base.Render(writer);
}
}
}
If the request is normal, it just renders whatever the contents are as usual. If you happen to be in the middle of an asynchronous postback, it uses the RegisterStartupScript API.
<asp:UpdatePanel ID="up1" runat="server">
<ContentTemplate>
<i88:InlineScript runat="server">
<script type="text/javascript">
alert('hi');
</script>
</i88:InlineScript>
<asp:Button ID="cmd" runat="server" Text="Update" />
</ContentTemplate>
</asp:UpdatePanel>
You get the beloved alert dialog when the update panel updates! Thankfully, because you still put the script element itself in the html, you still get javascript intellisense and all that jazz, too.
Simple, not terribly useful, probably not the best thing to do performance, but could be pretty handy in the right situation.
UPDATE: If you actually use this, you may want to add a check for sm == null as well as IsInAsyncPostBack, so that the control works even if there's no ScriptManager on the page.