Injecting Client-Side Script from an ASP.NET Server Control

 

Scott Mitchell

August 2003

Applies to:
    Microsoft® ASP.NET

Prerequisites: This article assumes the reader is familiar with ASP.NET.

Level of Difficulty: 2

Summary: While, technically, all of an ASP.NET server control's functionality can be performed on the server-side, often the usability of a server control can be greatly enhanced by adding client-side script. In this article we'll examine two means by which server controls can emit client-side script. We'll also build two server controls that utilize these techniques: PopupGreeting, a server control that displays a client-side, modal dialog box with a specified message on a Web page's first load, and ConfirmButton, which is an enhanced Button Web control that, when clicked, prompts the user with a JavaScript confirm() dialog box before posting back the Web Form. (11 printed pages)

Download InjectingClientSideScript.msi.

Contents

Introduction
Adding Client-Side Script Blocks with RegisterStartupScript() and RegisterClientScriptBlock()
Examining IsStartupScriptRegistered() and IsClientScriptBlockRegistered()
Emitting Client-Side Script Blocks from an ASP.NET Server Control
Emitting HTML Attributes for a ASP.NET Server Web Control
Conclusion

Introduction

While, technically, all of a Microsoft® ASP.NET server control's functionality can be performed on the server-side, often the usability of a server control can be greatly enhanced by adding client-side script. For example, the ASP.NET validation Web controls perform all validation checks on the server-side. However, for uplevel browsers, the validation Web controls also emit client-side script so that validation can be performed on the client-side as well. This means that users of those browsers get a more responsive, dynamic experience.

When developing ASP.NET server controls you should ask yourself how you could enhance the usability through the use of client-side script. Once you have identified these areas, all that remains is to augment the server control so that it emits the proper client-side script.

There are two types of client-side script ASP.NET server controls can emit:

  • Client-side script blocks
  • Client-side HTML attributes

Client-side script blocks are typically written in JavaScript, and usually contain functions that are executed when certain client-side events transpire. Client-side HTML attributes provide a way to tie a client-side event with a piece of client-side script. For example, the following HTML page contains a client-side script block that contains a function called doClick(). The page also contains a button—created by the <input> HTML element—that has its onclick attribute wired up to the doClick() function. That is, whenever a user clicks the button, the client-side code in the doClick() function will execute. In this example, a popup dialog box will display (Figure 1).

<html>
  <body>
    <form>
      <script language="JavaScript">
      <!--
         function doClick() {
            alert("You clicked me!");
         }
      // -->
      </script>

      <input type="button" onclick="doClick()" value="Click Me!" />
    </form>
  </body>
</html>

Figure 1 shows a screenshot of this HTML page when the Click Me! button is clicked.

Aa478975.aspnet-injectclientsidescript-01(en-us,MSDN.10).gif

Figure 1. Popup dialog box that displays when Click Me! Button is clicked

There are a couple of things worth mentioning in the client-side script in the HTML page above. First, note that the client-side script block is encased in HTML comments (<!-- and -->). These comments are in place because old, non-script aware browsers will simply display the contents of the <script> block if it is not encased in HTML comments. Furthermore, note that the closing HTML comment in the script block has a JavaScript comment preceding it—//. This is because older versions of Netscape would throw a JavaScript parsing exception when the --> was encountered, unless it was commented out. Fortunately, modern browsers do not require this extra pampering, so if you are developing Web pages for an intranet or other browser-controlled environment, you need not take such precautions.

For those unfamiliar with client-side scripting, the alert(string) function simply displays a modal popup dialog box that contains the message specified by the string parameter. HTML elements all have a number of client-side attributes (such as onclick, onmouseover, onmouseout, onfocus, onblur, and so on) that can be assigned a piece of client-side JavaScript code. For example, in the HTML page above, the <input> element's onclick attribute is wired up to the doClick() function, thereby causing the doClick() function to execute when the button is clicked. A list of JavaScript events and their associated HTML attributes can be found in the article Introduction to Dynamic HTML. For more information on client-side JavaScript, refer to the article HTML and Dynamic HTML.

In this article we will see how to emit both client-side script blocks and HTML element attributes in ASP.NET server controls. First, we'll see how to use two methods in the System.Web.UI.Page class to add client-side script blocks to an ASP.NET Web page: RegisterStartupScript() and RegisterClientScriptBlock(). Armed with this knowledge, we'll examine building a simple server control that displays a client-side popup dialog box whenever the page is loaded. After this, we'll turn our attention to adding HTML attributes to the HTML element rendered by the ASP.NET server control. Finally, we'll put all that we've learned to practice and build a ConfirmButton Web control—one that, when clicked, prompts the user with a client-side confirm dialog box that asks if they are sure they want to proceed.

Adding Client-Side Script Blocks with RegisterStartupScript() and RegisterClientScriptBlock()

The System.Web.UI.Page class contains two methods for emitting client-side script code into the HTML rendered by the ASP.NET Web page:

  • RegisterStartupScript(key, script)
  • RegisterClientScriptBlock(key, script)

Both of these methods take two strings as input. The second parameter, script, is the client-side script—including the opening and closing <script> tags—to insert into the page. The first parameter, key, serves as a unique identifier for the inserted client-side script.

The only difference between these two methods is where each one emits the script block. RegisterClientScriptBlock() emits the script block at the beginning of the Web Form (right after the <form runat="server"> tag), while RegisterStartupScript() emits the script block at the end of the Web Form (right before the </form> tag).

To better understand why there are two different methods for emitting client-side script, realize that client-side script can be partitioned into two classes: code that is designed to run immediately when the page is loaded, and code that is designed to run when some client-side event occurs. A common example of code that is designed to run when the page is loaded is client-side code designed to set the focus to a textbox. For example, when you visit Google, a small bit of client-side code is executed when the page is loaded to automatically set the focus to the search textbox.

An example of code that is designed to run in response to a client-side event can be seen below. Specifically, in this example, a popup dialog box displays when a button is clicked:

<html>
  <body>
    <form>
      <script language="JavaScript">
      <!--
        function displayPopup() {
            alert("Hello, world.");
        }
      // -->
      </script>

      <input type="button" value="Click Me!" onclick="displayPopup()" />
    </form>
  </body>
</html>

Here, the onclick="displayPopup()" in the <input> tag indicates that when the button is clicked the JavaScript function displayPopup() should run.

The RegisterStartupScript() method is useful for adding script blocks that are designed to run when the page is loaded. The script blocks added via this method appear at the end of the Web Form because the HTML element the script modifies must be defined prior to the script running. That is, if you want to use client-side script to set the focus to a textbox, you must make certain that the textbox's HTML markup appears before the script that sets the textbox's focus. For example, the following HTML will display a textbox and set the focus to the textbox:

<input type="text" id="myTextBox" />

<script language="JavaScript">
<!--
   document.getElementById("myTextBox").focus();
// -->
</script>

Whereas the following HTML will not set the focus to the textbox, because the textbox is defined after the script block:

<script language="JavaScript">
<!--
   document.getElementById("myTextBox").focus();
// -->
</script>

<input type="text" id="myTextBox" />

Therefore, the RegisterStartupScript() method places the <script> block at the end of the Web Form to ensure that all HTML elements in the Web Form have been declared by the time the client-side script is executed.

The RegisterClientScriptBlock() method should be used for script code that executes in response to a client-side event. The script blocks emitted by this method are emitted at the start of the Web Form since it is not imperative that the script blocks be placed after all of the HTML elements.

Examining IsStartupScriptRegistered() and IsClientScriptBlockRegistered()

In addition to the RegisterStartupScript() and RegisterClientScriptBlock() methods, the Page class contains two helper methods commonly used when emitting client-side script:

  • IsStartupScriptRegistered(key)
  • IsClientScriptBlockRegistered(key)

Recall that when inserting a client-side script block with either RegisterStartupScript() or RegisterClientScriptBlock(), a key is provided that uniquely identifies the script block. These methods, both of which take in a single input—a string key—and return a Boolean value, indicate whether or not a script block with the specified key has already been added to the page. Specifically, the methods return True if a script block with the specified key has already been registered, and False otherwise.

To understand the utility of these two methods, consider the ASP.NET validation Web controls RequiredFieldValidator, RegularExpressionValidator, and so on. These controls rely on a common validation JavaScript file, WebValidation.js, which is found in the aspnet_client/system_web/version_number directory of an ASP.NET Web application. Therefore, each of these controls emits an identical script block that calls the appropriate JavaScript function defined in the WebValidation.js file to start the client-side validation process. These controls accomplish this by using the Page class' RegisterClientScriptBlock() method, using the key ValidatorIncludeScript.

Next consider what happens when there are multiple validation Web controls on a single ASP.NET Web page. Each of these Web controls wants to emit an identical script block with an identical key. If the RegisterClientScriptBlock() or RegisterStartupScript() method is called twice with the same key, the second call is considered a duplicate script block and is ignored. Therefore, even with multiple validation controls on a single Web page, only one instance of the common script block will be emitted. However, realize that all of the validation Web controls other than the first one that rendered will have wasted their time in building up the common client-side script to be emitted.

This is where the IsClientScriptBlock() and IsStartupScript()methods come in handy. Rather than take the time to construct the client-side code to be emitted, the validation Web controls first check to see if there already exists a script block registered with the key ValidatorIncludeScript. If there is, then the control can bypass construction of the client-side script block, as it has already been completed by some other validation control on the page.

Therefore, whenever constructing client-side script, it is always wise to first call the IsClientScriptBlock() or IsStartupScript()method to determine if generating the client-side script is necessary. We'll see examples of using the IsClientScriptBlock() and IsStartupScript()methods in tandem with RegisterClientScriptBlock() and RegisterStartupScript() in the next section.

Emitting Client-Side Script Blocks from an ASP.NET Server Control

Keep in mind that the RegisterStartupScript() and RegisterClientScriptBlock() methods are methods of the System.Web.UI.Page class. Fortunately, it is easy to call these methods from an ASP.NET server control because the System.Web.UI.Control class, the class from which all ASP.NET server controls are either directly or indirectly derived, has a property called Page that contains a reference to the Page instance, which contains the server control. Therefore, in order to add a client-side script block from an ASP.NET server control, all you have to do is use the following syntax:

this.Page.RegisterClientScriptBlock(key, script);

Typically adding client-side script blocks is a task handled in the OnPreRender() method, which is the method that executes during the pre-rendering stage of the control's lifecycle.

Let's create an ASP.NET server control that simply displays a client-side popup dialog box. This example will illustrate how easy it is to build a control that emits client-side script.

Start by creating a new Web Control Library project in Microsoft® Visual Studio® .NET. This will create a new project with a single class that is derived from System.Web.UI.WebControls.WebControl. However, we want to have this class derived from the System.Web.UI.Control class instead. To understand why, understand that the WebControl class was designed to support server controls that render as HTML elements, while the Control class was designed for server controls that do not result in a rendered HTML element.

Most of the built-in ASP.NET server controls emit an HTML element. For example, the TextBox Web control emits an <input> element with its type property set to text; the DataGrid Web control emits a <table> element, with <tr> elements for each record to be displayed and <td> columns for each field. However, not all server controls necessarily emit an HTML element. For example, the Literal control merely outputs its Text property as-is, without wrapping it in an HTML element. Similarly, the Repeater does not encase its output in an HTML element. Those server controls that render as an HTML element—TextBox, Button, DataGrid, and so on—are derived from the System.Web.UI.WebControls.WebControl class, whereas those controls that do not produce an HTML element—Literal, Repeater, and so on—are derived from the System.Web.UI.Control class.

Since the server control we'll be creating has no visual aspect (it merely emits a client-side script block that displays a popup control), it would be best for this control to be derived from System.Web.UI.Control as opposed to System.Web.UI.WebControls.WebControl.

This control will need only two properties:

  • PopupMessage—a string that indicates the message to be displayed in the popup dialog box
  • Enabled—a Boolean that indicates if the control is enabled or not. If the control is enabled, then the popup dialog box is displayed; otherwise, it is not displayed. (The reason we have to add an Enabled property is because the Control class that we are deriving this control from does not include the Enabled property; this property is only present implicitly for those controls that are derived from WebControl.)

In addition to these two properties, we need to override the OnPreRender() method. Here, we need to make a call to RegisterStartupScript(), passing in a key unique to the control and the suitable client-side script to display the popup dialog box. The complete code for this class can be seen below:

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;

namespace ClientSideScript
{
   /// <summary>
   /// Summary description for WebCustomControl1.
   /// </summary>
   [DefaultProperty("Text"),
      ToolboxData("<{0}:PopupGreeting runat=server></{0}:PopupGreeting>")]
   public class PopupGreeting : System.Web.UI.Control
   {
      [Bindable(true),
         Category("Appearance"),
         DefaultValue("")]
      public string PopupMessage
      {
         get
         {
            // See if the item exists in the ViewState
            object popupMessage = this.ViewState["PopupMessage"];
            if (popupMessage != null)
               return this.ViewState["PopupMessage"].ToString();
            else
               return "Welcome to my Web site!";
         }

         set
         {
            // Assign the ViewState variable
            ViewState["PopupMessage"] = value;
         }
      }

      [Bindable(true),
      Category("Appearance"),
      DefaultValue("")]
      public bool Enabled
      {
         get
         {
            // See if the item exists in the ViewState
            object enabled = this.ViewState["Enabled"];
            if (enabled != null)
               return (bool) this.ViewState["Enabled"];
            else
               return true;
         }

         set
         {
            // Assign the ViewState variable
            ViewState["Enabled"] = value;
         }
      }


      protected override void OnPreRender(EventArgs e)
      {
         base.OnPreRender(e);

string scriptKey = "intoPopupMessage:" + this.UniqueID;

         if (!Page.IsStartupScriptRegistered(scriptKey) && this.Enabled &&
                      !Page.IsPostBack)
         {
            string scriptBlock = 
               @"<script language=""JavaScript"">
               <!--
                  alert(""%%POPUP_MESSAGE%%"");
               // -->
               </script>";
            scriptBlock = scriptBlock.Replace("%%POPUP_MESSAGE%%", this.PopupMessage);

            Page.RegisterStartupScript(scriptKey, scriptBlock);
         }
      }
   }
}

Take note of these two things: first, the properties Enabled and PopupMessage are saved in the ViewState. This allows these values to be persisted across postbacks. Next, in the OnPreRender() method, the key used for the script block is the text intoPopupMessage: concatenated with the control's UniqueID property. If a single, hard-coded key were used, then, if there were multiple controls on the page, only the first control would be able to register its script block, so only one popup dialog box would be displayed. By using the UniqueID in the script block key, each instance of this control is guaranteed to get its script block in.

Before registering the script block, the code first checks three conditions:

  1. That there is not already a script registered with the same key. This should never be possible, since each control instance should have a UniqueID property value. However, it never hurts to get into the practice of using the IsStartupScriptRegistered() method before actually taking the time to create and register the startup script.
  2. If the control's Enabled property is True.
  3. If the page is not being posted back. This code has the popup dialog box only display on the page's first load. It is likely not the intention to have this popup displayed each time the page is posted back, but rather, only displayed on the first page load. A potential enhancement would be to add a Boolean property to this control to allow the user to specify if the popup dialog box should be generated on postbacks as well.

If these three conditions pass, then the script is specified and the PopupMessage property value is inserted into the script in the proper location. Finally, the Page property's RegisterStartupScript() method is called, passing in the key and script code.

The PopupGreeting code is available in a download at the end of this article. This download includes the Visual Studio .NET Solution named ClientSideControlsAndTester, which contains two projects:

  • ClientSideControls, which contains the PopupGreeting server control
  • ClientSideTester, which contains an ASP.NET Web application designed to test the ClientSideControls

The compiled assembly for the ClientSideControls project is named ClientSideControls.dll. To use the PopupGreeting server control in your own ASP.NET Web application, add the ClientSideControls.dll file to your Web application's References. Next, in the Designer, right-click on the Toolbox and choose Add/Remove Items . . .. Again, select the ClientSideControls.dll file. This will add a new item to the Toolbox titled PopupGreeting. You can then drag and drop the control from the Toolbox onto the Designer.

Figure 2 shows a screenshot of Visual Studio .NET after the PopupGreeting control has been added to the Toolbox and then added to the Designer. The PopupGreeting control in the Toolbox is circled in red, the PopupGreeting output in the Designer is circled in blue, and the properties of the PopupGreeting can be seen in the Properties pane in the right-hand side of the screenshot.

Aa478975.aspnet-injectclientsidescript-02(en-us,MSDN.10).gif

Figure 2. The PopupGreeting Server Control has been added to an ASP.NET Web form page

Emitting HTML Attributes for an ASP.NET Server Web Control

Recall that there are two ways to emit client-side script through a server control:

  • Through the use of client-side script blocks
  • Through HTML element attributes

In the previous section we examined how to add client-side script blocks to an ASP.NET Web page using the Page class's RegisterStartupScript() and RegisterClientScriptBlock() methods. In this final section we'll see how to add HTML element attributes to the HTML element rendered by the server control.

Before we begin, realize that typically this approach will only be used for server controls that are derived from the System.Web.UI.WebControls.WebControl class, as controls derived from this class emit some HTML element. Server controls that do not emit an HTML element—like the PopupGreeting server control from the previous section—do not ever need to write out HTML element attributes because they do not write out an HTML element to begin with.

The WebControl class contains a method for adding HTML element attributes to the HTML element being emitted by the Web control. This method is called AddAttributesToRender() and has a single input parameter, an HtmlTextWriter instance. To add HTML attributes to the Web control you can use one of these two methods from the HtmlTextWriter:

  • AddAttribute()
  • AddStyleAttribute()

The AddAttribute() method adds an HTML attribute like title, class, style, onclick, and so on to the HTML element. AddStyleAttribute(), on the other hand, adds style settings to the HTML element, like background-color, color, font-size, and so on.

AddAttribute() has a few overloaded forms, but in the code we'll examine we'll use the following form: AddAttribute(HtmlTextWriterAttribute, value). The first parameter, HtmlTextWriterAttribute, needs to be a member from the HtmlTextWriterAttribute enumeration. This enumeration contains items like Align, Bgcolor, Class, Onclick, and so on. You can see a complete listing in the .NET Framework Class Library, HtmlTextWriterAttribute Enumeration. The value input parameter specifies the value assigned to the specified HTML attribute. Finally, if you want to add an HTML attribute that is not defined in the HtmlTextWriterAttribute enumeration, you can use an alternate form of the AddAttribute() method, AddAttribute(attributeName, value). Here, both attributeName and value are strings.

To apply this information, let's create a server Web control that renders as a confirm button. A confirm button is a submit button that, when clicked, displays a popup dialog box asking the user if they're certain they want to continue. This gives the user a chance to click Cancel, which has the effect of not submitting the form. Such functionality is particularly useful when there are buttons to delete information—nothing can be more upsetting to an end user (or Web site administrator) than to have deleted an item from a database due to an accidental and unfortunate mouse click.

To save ourselves a lot of work, we can have the ConfirmButton Web control be derived from the System.Web.UI.WebControls.Button class, since this class already does all the heavy lifting involved with rendering a submit button. All that we need to do in our derived class is add a property so that the user can specify the confirmation message, then override the Button's AddAttributesToRender() method, and then add an attribute to handle the onclick client-side event.

Start by creating a new Web Control Library project in Visual Studio .NET, or add a new Web Custom Control into the ClientSideControls project. The complete source code for the ConfirmButton class can be seen below:

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;

namespace ClientSideControls
{
   /// <summary>
   /// Summary description for ConfirmButton.
   /// </summary>
   [DefaultProperty("Text"), 
      ToolboxData("<{0}:ConfirmButton runat=server></{0}:ConfirmButton>")]
   public class ConfirmButton : Button
   {
      [Bindable(true), 
         Category("Appearance"), 
         DefaultValue("")] 
      public string PopupMessage
      {
         get
         {
            // See if the item exists in the ViewState
            object popupMessage = this.ViewState["PopupMessage"];
            if (popupMessage != null)
               return this.ViewState["PopupMessage"].ToString();
            else
               return "Are you sure you want to continue?";
         }

         set
         {
            // Assign the ViewState variable
            ViewState["PopupMessage"] = value;
         }
      }


      protected override void AddAttributesToRender(HtmlTextWriter writer)
      {
         base.AddAttributesToRender(writer);

         string script = @"return confirm(""%%POPUP_MESSAGE%%"");";
         script = script.Replace("%%POPUP_MESSAGE%%", 
            this.PopupMessage.Replace("\"", "\\\""));

         writer.AddAttribute(HtmlTextWriterAttribute.Onclick, script);
      }
   }
}

The first thing to notice is that this class, ConfirmButton, is derived from the Button class. Since the Button class already contains all the properties and methods a Button Web control uses, all we have to do is add the properties and methods to make the Button, when clicked, display a confirm dialog box. We now need one property, PopupMessage, which is the message that will display in the confirm popup dialog box. By default, this message is, "Are you sure you want to continue?" If you are using the ConfirmButton for verifying deletes, you might want to change the message to something like, "This action will permanently delete the selected item. Are you sure you want to do this?"

We only need to override a single method, AddAttributesToRender(). In this method we simply construct the client-side JavaScript to execute when the rendered <input> element's onclick event fires, and then add this via the AddAttribute() method of the passed-in HtmlTextWriter object. One thing to note in this method is that we must replace all instances of double-quotes in the PopupMessage property value with escaped double-quotes (namely, \"). Also, realize that the AddAttribute() by default HTML encodes the characters in the second parameter. That is, an ASP.NET Web page with a ConfirmButton whose PopupMessage property is set to "Do you want to continue?" will emit the following HTML markup:

<input type="submit" name="ConfirmButton1" 
value="Click Me!" id="ConfirmButton1" onclick="return confirm
(&quot;Do you want to continue?&quot;);" />

If you are unfamiliar with JavaScript's confirm(string) function, it simply accepts a string parameter and displays a modal dialog box with the specified string. This dialog box contains two buttons, OK and Cancel. If OK is clicked, the confirm() function returns True, otherwise it returns False. Note that the onclick event returns the result of the confirm() function call. When a form is submitted by clicking a submit button, if the submit button's onclick event returns False, the form is not submitted. Hence, the confirm() function can be used in this manner to only submit the form if the user gives his confirmation. For more information on confirm(), see Javascript Confirm Form Submission from the ASP Warrior site.

Aa478975.aspnet-injectclientsidescript-03(en-us,MSDN.10).gif

Figure 3. The ConfirmButton in action

While ConfirmButton uses inline JavaScript in the button's onclick event handler, another option is to create a function in a client-side script block in the ConfirmButton's OnPreRender() method, and then adjust the onclick attribute to call this function.

Conclusion

In this article we examined two methods for injecting client-side script via an ASP.NET server control. The first method is to insert client-side script blocks using the Page class's RegisterStartupScript() and RegisterClientScriptBlock() methods. The second method is to add client-side script to an HTML element's attributes. This is accomplished by overriding the Web server control's AddAttributesToRender() method, and using the HtmlTextWriter's AddAttribute() method.

We also examined in this article two simple server controls that utilize client-side script to improve their functionality. The PopupGreeting control simply displays a modal popup dialog box when the page was first loaded. Similarly, the ConfirmButton Web control prompts the user to confirm that they wish to continue when they submit a form by clicking on the button.

You can greatly improve the user's experience by inserting client-side script into your custom server controls. While the two server controls examined in this article were relatively simple and won't win any awards for usability or ingenuity, at MetaBuilders.com there is an impressive display of the capabilities that can be realized with client-side script injection from an ASP.NET server control. Specifically, at MetaBuilders.com you can find server controls that automatically add the focus to a textbox, move items between two drop-down lists, add and remove items from a drop-down list, display parent-child related data in a series of drop-down lists, and on and on. Best of all, these controls are free and include the complete source code.

Happy Programming!

About the Author

Scott Mitchell, author of five ASP/ASP.NET books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies for the past five years. An active member in the ASP and ASP.NET community, Scott is passionate about ASP and ASP.NET and enjoys helping others learn more about these exciting technologies. For more on the DataGrid, DataList, and Repeater controls, check out Scott's book ASP.NET Data Web Controls Kick Start (ISBN: 0672325012).

© Microsoft Corporation. All rights reserved.