Executing JavaScript after a Partial Render
I had a fun day debugging some ASP.NET plus jQuery this week, and came across something I’ve known is possible for some time, but that I’ve never actually needed to do... and that was to ensure a bit of JavaScript ran once an UpdatePanel had refreshed as part of a partial render.
It turns out that this is easy; all you need to do is register your JavaScript code block with some methods on ScriptManager, rather than using the standard ClientScript methods.
For example, consider the HTML below (I’ve omitted the rest of the page including the script manager, but it is in the download);
<asp:Button
ID="ClickMeButton"
runat="server"
Text="Click Me to Refresh Below"
onclick="ClickMeButton_Click" />
<div style="background: black; color: White">
<asp:UpdatePanel
ID="MyUpdatePanel"
runat="server"
UpdateMode="Conditional">
<Triggers>
<asp:AsyncPostBackTrigger
ControlID="ClickMeButton"
EventName="Click" />
</Triggers>
<ContentTemplate>
Last Updated:
<asp:Label
ID="UpdateTimeLabel"
runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
</div>
<div id="log">
</div>
Here we have a simple UpdatePanel, with a button that triggers the async postback. There is also an empty DIV with ID “log”.
In the server-side code of the button click event I have some code that looks a bit like this;
string time = DateTime.Now.ToLongTimeString();
ScriptManager.RegisterStartupScript(
UpdateTimeLabel,
typeof(Label),
"UpdateTimeLabelStartupScript",
CreateScript(time),
true);
The CreateScript function simply returns some JavaScript in a string that reads as follows (the {0} is replace with a time);
$('#log').append('Last Server Run: {0}<br />');
This uses jQuery to append a message to our “log” DIV.
There is also an alternative though; using the Sys.Application.add_load method to attach a page load script function, or creating a pageLoad JavaScript function that is a synonym for the same thing. I’ve done the latter;
function pageLoad() {
$('#log').append('pageLoad executed<br />');
}
This function is executed when the page initially renders and when every partial render completes. This means the content of my “log” DIV rapidly starts to look like this;
pageLoad executed
Last Run: 21:56:12
pageLoad executed
Last Run: 21:56:14
pageLoad executed
Last Run: 21:56:19
pageLoad executed
Easy huh?
I hope that adds some clarity to your options for executing script as the result of a partial postback. Check out the attached if you want to try it yourself.
Comments
Anonymous
September 28, 2009
Hi Simon, I'm pretty novie as far as this topic goes and i have a rendering problem with a script i'm using on a web page. I'm using the x.js to measure 3 column lengths and make them all the same size. however when i use an update panel and call information from my SQL database the script runs after it has updated but before it has chance to render. I tried putting a delay on the page load script which fixes it on a quick connection but not on a slow one. pretty useless i know :) my code that loads the script after the update panel on the page load event looks like this: protected void Page_Load(object sender, EventArgs e) { Page.ClientScript.RegisterClientScriptInclude("selective", ResolveUrl(@"Scriptsx.js")); System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.Append(@"<script language='javascript'>"); sb.Append(@"adjustLayout();"); sb.Append(@"</script>"); System.Threading.Thread.Sleep(2000); ScriptManager.RegisterStartupScript(this, this.GetType(), "ajax", sb.ToString(), false); } I'm sure i'm making a school boy error and i've been pulling my hair out for a while now. any comments would be greatly appreciated! thanks AdrianAnonymous
September 28, 2009
@ Adrian, There's a few things that could be. Firstly - lose the Thread.Sleep; that's pausing the rendering of the HTML etc on the server (it is usually buffered and sent to the client when all server side processing is complete) so would massively reduce your app's scalability. If you wanted to delay tasks on the client check out "window.setTimeout". But in this case, I think the issue could be one of a few things; ScriptManager.RegisterStartupScript requires that the first two references (this and this.GetType() in your example) are to a control that is inside the update panel; otherwise the script won't be emitted. Try using; ScriptManager.RegisterStartupScript(this.MyButton, typeof(Button).... etc. Secondly, it might be a side effect of how the script is executed. Your startup script will be being executed by the client script loader, not just by being present in the file. I'm not certain exactly when this executes script without looking, but there is an outside chance it could just be before the DOM is updated I guess... Instead you could use a script inside your "x.js" that is included every time the page is loaded (I'd just reference it with an <asp:scriptreference> block inside the ScriptManager control's markup in the HTML). Then, your code in "x.js" might look like this; function adjustLayout() { // some code here } Sys.Application.add_load(adjustLayout); This tells it to fire adjustLayout every time the page loads or an async postback completes. Hope that helps - let me know if it works. SimonAnonymous
September 29, 2009
Hi Simon, Thanks so much for your quick response and valuable input, i am please to announce It works! :) ... well sort of! I realise the Thread.Sleep was not a good thing but it was my feable attempt to fix it! The problem i was having wasn't with the update panel as such, the script did run after the update panel displayed, it just did it too quickly. I didn't explain my issue fully and i apologise. The problem was that the information called from an SQL database referenced a picture and the picture wasn't considered part of the update panel. It simply returned HTML code to render it. I realise this is less than ideal but i'm new and still learning. In the end I used your suggestion of pausing it on the clients machine by using this: function delayedAlert() {timeoutID = window.setTimeout(adjustLayout, 4000);} I then changed the page load event to run this new function in place of the adjustLayout. I realise that this still isn't a complete fix because if the image download takes longer than 4 seconds then it will not rerun the script but i am emensly happy to have improved the situation even just a little! Thank you very much, without your reply I would never have known how to delay a script on the clients computer, as i thought i was already doing by using Thread.Sleep. Much appreciated! ThanksAnonymous
October 02, 2009
@ Adrian, no problem, pleased you've got it working! I would wave the cautionary flag that you refer to though - that this means a user must wait at least 4 seconds, and that if the browser takes longer it won't work. If you can, a more robust approach is to locate the correct event to subscribe to, but I acknowledge that can be a bit tricky. Great to hear you've made progress though! Simon