Recently, while developing a Web application, I encountered a problem with users who have either purposely or inadvertently disabled JavaScript on their browsers. Although .NET provides a way to verify browser support for Javascript through the use of 'Request.Browser.JavaScript', it does not tell us if JavaScript is enabled. So how do we test if the client has JavaScript enabled?
Fortunately there are many solutions on the Web, and a quick search netted many possible answers. Most techniques available on the Web used some form of the following: use JavaScript to change a value in a form and then submit the form via JavaScript. After the submit, check the value and compare it to the original. If it changed, then JavaScript is enabled. If it didn't change, then either JavaScript isn't supported or JavaScript isn't enabled.
However, all the solutions I found on the Web failed the two criteria I was looking for: integration with ASP.NET and re-usability. I didn't want to have to change JavaScript code in each project or file that I was involved in; I wanted something more elegant. So after some thought on how I could shape the existing solutions to work with ASP.NET, I came to the conclusion that a custom server control would be the best solution.
There were several small problems with doing this as a server control: how to handle the ASP.NET postback and viewstate model and how to have the server control emit client-side JavaScript. In this article we will make a small and simple server control that will allow us to achieve two simple goals: 1) determine if JavaScript is enabled and 2) allow for re-usability of the server control on multiple projects. Fortunately Microsoft has done an excellent job of documenting the required objects and interfaces we needed to inherit from and implement in order to write our own server control.
One of the first decisions we need to make when designing a server control is whether we will inherit from Control or WebControl. Why does this matter? Well, the simpler I can make my objects, the better. Because WebControl implements attributes such as Font and ForeColor, which are unnecessary in our control, I decided that the simpler Control object would be a better choice from which to inherit.
A standard control defines the properties, methods, and events that are shared by all ASP.NET server controls, including WebControls which inherit from Control. Some of the basic attributes that the Control object passes on to its sub-classes are ID, UniqueID, and ViewState, which we do need in our new server control.
There is also an interface that we need to implement: IPostbackDataHandler. According to Microsoft, this interface "...defines methods that ASP.NET server controls must implement to automatically load post back data." We want to implement this interface in order to capture when the client-side script changes the default form value. If the default value changes, then the control will raise an event that the default value has changed.
Implementation
So how are we going to test that JavaScript is enabled, exactly? By using JavaScript when the page is loaded to alter the value of a hidden input element and then submit the form, we can verify that JavaScript is enabled. If the hidden input element does not change its value, then we know that JavaScript is not enabled. The script below implements this:
An obvious problem arises with the above code, viewstate. If our server control is emitting the hidden input element and the JavaScript, then we need to participate in the postback process, hence the use of the IPostbackDataHandler interface. Another problem occurs here that we need to be aware of. Because we are changing the value of a hidden element in the form, if the user decides to disable JavaScript after our server control has verified that JavaScript is enabled, the server control will not report this change back unless a new page is loaded first.
This is an implementation problem that comes to light when the JavaScript code is emitted: the name of the form. How can we determine the name of the form so that we don't have to re-write the server control every time we have a new form name? After some serious investigation and an examination of ASP.NET's own "__doPostback" JavaScript function, I determined that the only way was to loop through all the controls on the page. By examining the type of each control until I found the type HtmlForm control, a simple query of the ClientID attribute would result in the form name. A private method of the server control is tasked with this responsibility:
private string GetFormName(Control parentControl)
{
string Name = "";
foreach(Control childControl in parentControl.Controls)
{
if (childControl.GetType().ToString() == "System.Web.UI.HtmlControls.HtmlForm")
{
Name = childControl.ClientID;
break;
}
else
{
if (childControl.HasControls())
{
Name = GetFormName(childControl);
}
}
}
return Name;
}
Now that we have the method to test if JavaScript was enabled, determined the limitations of the control, and figured out how to find the form name, we just have to put it all together into a server control.
Following Microsoft's directions, I had two events to override (OnInit and OnPreRender) and an interface to implement (IPostbackDataHandler.LoadPostData, IPostbackDataHandler.RaisePostDataChangedEvent).
Because we are going to examine postback data (remember we are submitting the form via JavaScript), we need to register our control as participating in the postback cycle. To do this, we override and add the following code to the OnInit event:
Now that the control is registered, we have to implement the interface IPostbackDataHandler. The LoadPostData method of this interface is called after the load event in the control lifecycle. It is in this method that we compare the postback value to the default value and determine if the value has changed. If it has, we return true so that the change event, handled by the RaisePostDataChangedEvent, is called, and the appropriate event that we have defined is raised.
At this point, we need to add a hidden type element and the JavaScript to the HTML document. The Page object in ASP.NET, which represents the page that our server control is a member of, has several useful methods for server controls to handle state and client side scripts. In the OnPreRender event we add the hidden type element to the HTML form by calling the Page.RegisterHiddenField method and passing to it the name of the hidden type element and its default value. Next, we call the Page.RegisterStartupScript which adds the JavaScript to the end of the page so that it executes after the page is complete. Below is the complete code for the event:
At this point, go ahead and compile the server control and fix any errors that might have occurred. See my included source code for a complete source file of the server control.
Creating a Test Harness
Now that the control is complete, we need to test it in an ASP.NET Web application. Create a new Web project in Visual Studio .NET, and add your newly created server control to the Toolbox:
Right-click in the Toolbox and select the 'Customize Toolbox...' from the context menu.
Click on the tab called '.NET Framework Components'.
Browse to the bin directory of the new server control and select the control's dll.
Click OK and you're done.
You should now see the server control in your ToolBox
and a new reference to the control has been added to your web application, visible in the Solution Explorer window.
Next, drag the control to your Web form and release. All that's left now is to add the appropriate code-behind. Below is a sample of what your HTML might look like after dragging the control to the HTML page:
Since we raise an event in the Page's lifecycle when the default value is changed (thus we know that JavaScript is enabled), we need to add two items to the code behind for our Web page. First, we need to setup an event handler for our server control's EnabledChanged event. That code can be added in several places; I've added mine to the OnInit event of the page:
JavascriptTest1.EnabledChanged += new System.EventHandler(this.Enabled_Changed);
From the code above, you can tell that we also need to write the method Enabled_Changed:
private void Enabled_Changed(object senderm, System.EventArgs e)
{
Response.Write("<br>Hey, the event was raised!");
}
We've also added in the server control an attribute called "Enabled", which returns true or false depending on the ViewState of the hidden type element. We could use this property in the Page_Load event if we wanted to determine on page load if the client has JavaScript enabled:
private void Page_Load(object sender, System.EventArgs e)
{
if (JavascriptTest1.Enabled)
{
Response.Write ("Browser supports Javascript and it IS enabled.");
}
else
{
Response.Write ("Browser does NOT support Javascript or it IS NOT enabled.");
}
}
Obviously the code above doesn't cover every possibility, but it does demonstrate the basic functionality of the "Enabled" attribute of the object during the Page_Load event. After compiling the Web project, and once again correcting any errors that may have occurred, view the page in a browser.
Conclusion
Although I haven't covered every detail involved in creating this server control, I have touched on each of the major points that are required to create one. The source code for this solution contains the Web application project and the server control project with detailed comments that explain each attribute and method of the server control. This server control can prove useful with those sites that require JavaScript to function properly. There are a few issues with this control, such as detecting when the client disables JavaScript after the control executes. Another issue that may be a problem with some ASP.NET sites is that this control causes the page to post to itself immediately after loading, if javaScript is enabled. A possible work around is to have the JavaScript change the form action from POST to GET and modify the server control to handle a GET rather than a POST.
George Masselli has been involved with Microsoft technologies for more than 7 years. After spending considerable time consulting with various companies such as Carter & Burgess, Microsoft, and Pier One, he currently contributes his talents to eLevel, Inc. His range of experience includes SQL Server, Oracle, Visual Basic, ASP, XML, XSL, C#, and VS.NET. He currently lives in Fort Worth, Texas, with his wonderful wife, Mary, and spoiled pets Cooper, Kelly, and Dante. He can be reached at gmasselli@yahoo.com .
Conrad Jalali shows how to build Web custom controls by creating one that displays checkboxes in a categorized, hierarchical view. [Read This Article][Top]
Conrad Jalali shows how to extend the functionality of the ASP.NET Calendar control to remove some of the annoying postback delays that occur when populating a text box with a date from a popup calendar. [Read This Article][Top]
This article covers some of the essentials of building reusable Web controls. Learn how to create a Web control and its custom events and event arguments, as well as how to use the control properly within a page. [Read This Article][Top]
ASP.NET provides only one control that supports paging, the DataGrid. Tomasz Kaszuba shows how to build and implement a custom pager for different controls that change depending on the data source or presentation. [Read This Article][Top]
Windows Forms within Web pages work in a manner similar to Java applets. Thiru Thangarathinam shows how to host Windows Forms controls in Internet
Explorer and how to utilize .NET Code Access Security to configure what the
control can do when running within the browser. [Read This Article][Top]
James Culshaw shares his experiences building his first working custom control, a basic mask control that allows input of time values, and offers advice and tips to those just starting out. [Read This Article][Top]
Rob Chartier offers a tour of the .NET Speech SDK Beta 2 and its use of the industry specification SALT. The article shows you how to create a basic speech-enabled Web user control. [Read This Article][Top]
In this article, Luther Stanton shows how to combine inheritance and server controls to create a self-populating drop-down-list control. [Read This Article][Top]
Solomon Shaffer explains custom controls, describes the complexities and issues surrounding building such controls, and walks through a useful example. [Read This Article][Top]
Mailing List
Want to receive email when the next article is published? Just Click Here to sign up.