Thursday, April 14, 2011

Difference between RegisterStartupScript and RegisterClientScriptBlock?

Is the only difference between the RegisterStartupScript and the RegisterClientScriptBlock is that RegisterStartupScript puts the javascript before the closing tag of the page and RegisterClientScriptBlock puts it right after the starting tag of the page? Also, when would you choose one over the other. I wrote up a quick sample page where I had an issue and I am not sure the exact reason of why it is happening. Here is the aspx markup:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
    <asp:Label ID="lblDisplayDate" runat="server" Text="Label">
    </asp:Label><br />

    <asp:Button ID="btnPostback" runat="server" 
                Text="Register Startup Script" 
                onclick="btnPostback_Click" />
    <br />
        <asp:Button ID="btnPostBack2" runat="server" 
                    Text="Register" 
                    onclick="btnPostBack2_Click" />
</div>
</form>
</body>
</html>

Here is the Code Behind:

public partial class _Default : System.Web.UI.Page 
{

protected void Page_Load(object sender, EventArgs e)
{
    lblDisplayDate.Text = DateTime.Now.ToString("T");
}

protected void btnPostback_Click(object sender, EventArgs e)
{
    System.Text.StringBuilder sb = new System.Text.StringBuilder();
    sb.Append(@"<script language='javascript'>");
    sb.Append(@"var lbl = document.getElementById('lblDisplayDate');");
    sb.Append(@"lbl.style.color='red';");
    sb.Append(@"</script>");

    if(!ClientScript.IsStartupScriptRegistered("JSScript"))
    {
        ClientScript.RegisterStartupScript(this.GetType(),"JSScript",
        sb.ToString());
    }
}

protected void btnPostBack2_Click(object sender, EventArgs e)
{
    System.Text.StringBuilder sb = new System.Text.StringBuilder();
    sb.Append(@"<script language='javascript'>");
    sb.Append(@"var lbl = document.getElementById('lblDisplayDate');");
    sb.Append(@"lbl.style.color='red';");
    sb.Append(@"</script>");

    if (!ClientScript.IsClientScriptBlockRegistered("JSScriptBlock"))
    {
        ClientScript.RegisterClientScriptBlock(this.GetType(), "JSScriptBlock",  
        sb.ToString());
    } 
 }
}

The problem is when I click the btnPostBack button, it does a postback and changes the label to red, but when I click the btnPostBack2, it does a postback, but the label color does not change to red. Why is this? Is it because the label is not initialized?

I also read that if you are using an update panel, you need to use ScriptManager.RegisterStartupScript, but if I have a master page, would I use ScriptManagerProxy?

From stackoverflow
  • Here's an old discussion thread where I listed the main differences and the conditions in which you should use each of these methods. I think you may find it useful to go through the discussion.

    To explain the differences as relevant to your posted example:

    a. When you use RegisterStartupScript, it will render your script after all the elements in the page (right before the form's end tag). This enables the script to call or reference page elements without the possibility of it not finding them in the Page's DOM.

    Here is the rendered source of the page when you invoke the RegisterStartupScript method:

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head id="Head1"><title></title></head>
    <body>
      <form name="form1" method="post" action="StartupScript.aspx" id="form1">
    <div>
    <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="someViewstategibberish" />
    </div>
    <div>
      <span id="lblDisplayDate">Label</span><br />
      <input type="submit" name="btnPostback" value="Register Startup Script" id="btnPostback" />
      <br />
      <input type="submit" name="btnPostBack2" value="Register" id="btnPostBack2" />
    </div>
    <div>
      <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="someViewstategibberish" />
    </div>
    <!-- Note this part -->
    <script language='javascript'>
    var lbl = document.getElementById('lblDisplayDate');
    lbl.style.color='red';
    </script>
    </form>
    <!-- Note this part -->
    </body>
    </html>
    

    b. When you use RegisterClientScriptBlock, the script is rendered right after the Viewstate tag, but before any of the page elements. Since this is a direct script (not a function that can be called, it will immediately be executed by the browser. But the browser does not find the label in the Page's DOM at this stage and hence you should receive an "Object not found" error.

    Here is the rendered source of the page when you invoke the RegisterClientScriptBlock method:

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head id="Head1"><title></title></head>
    <body>
      <form name="form1" method="post" action="StartupScript.aspx" id="form1">
      <div>
      <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="someViewstategibberish" />
      </div>
    <script language='javascript'>
    var lbl = document.getElementById('lblDisplayDate');
    // Error is thrown in the next line because lbl is null.
    lbl.style.color='green';
    

    Therefore, to summarize, you should call the latter method if you intend to render a function definition. You can then render the call to that function using the former method (or add a client side attribute).

    Edit after comments:


    For instance, the following function would work:

    protected void btnPostBack2_Click(object sender, EventArgs e) 
    { 
      System.Text.StringBuilder sb = new System.Text.StringBuilder(); 
      sb.Append("<script language='javascript'>function ChangeColor() {"); 
      sb.Append("var lbl = document.getElementById('lblDisplayDate');"); 
      sb.Append("lbl.style.color='green';"); 
      sb.Append("}</script>"); 
    
      //Render the function definition. 
      if (!ClientScript.IsClientScriptBlockRegistered("JSScriptBlock")) 
      {
        ClientScript.RegisterClientScriptBlock(this.GetType(), "JSScriptBlock", sb.ToString()); 
      }
    
      //Render the function invocation. 
      string funcCall = "<script language='javascript'>ChangeColor();</script>"; 
    
      if (!ClientScript.IsStartupScriptRegistered("JSScript"))
      { 
        ClientScript.RegisterStartupScript(this.GetType(), "JSScript", funcCall); 
      } 
    }
    
    Xaisoft : Can you explain a little more about the inline functions.
    Cerebrus : Editing my post to better illustrate it against your example.
    Xaisoft : I actually don't get an error, the time updates, but the color does not change. What about the part of my question where I ask if I have to use ScriptManagerProxy if I have a ScriptManager already defined in a master page?
    Cerebrus : Done editing. I'm not sure about the error with the ScriptManagerProxy. I think you should evaluate if that isn't really a separate question. ;-)
    Xaisoft : Great! Thanks so far. I am not getting an error with ScriptManagerProxy. I just know that you can only declare one instance of ScriptManager, so if I already have a ScriptManager defined in a master page for example, I would assume that I would use ScriptManagerProxy instead.
    Xaisoft : I understand what you are saying about RegisterstartupScript, but not quite sure what you mean by render a function definition. Are you saying that if I put the code where I create the script into a function and call that in the button click, it would work?
    Xaisoft : Or do you mean if I create a javascript function and in there change the label's color.
    Cerebrus : Edited again to explain! ;-)
    Xaisoft : Great! Thanks for your patience and explanations.
    Cerebrus : My pleasure, and thanks for the vote+acceptance. :-)
  • When we use a MasterPage we have to use a script manager proxy on our page , that is because there can be only one script manager . So the practice is to keep the scriptmanager in master and use the proxy in individual pages . hope this answers your question

0 comments:

Post a Comment