Client-Side Validation in Downlevel Browsers

Introduction

Last week I wrote an article called A Look at ASP.NET's Adaptive Rendering
that examined how the ASP.NET Web controls emit different HTML based on the browser that was visiting the page.
This technique is referred to as adaptive rendering. Adaptive rendering has its advantages in that, ideally,
appropriate markup is generated based on the requesting browser. However, by default ASP.NET is configured to send
HTML 4.0-compliant HTML to only Internet Explorer, and sends HTML 3.2-compliant HTML to all non-Microsoft browsers,
even though modern, non-Microsoft browsers such as Mozilla, Netscape 6.0+, Opera, and others can handle HTML 4.0.
In A Look at ASP.NET's Adaptive Rendering we saw how to configure a Web application or Web server to have the Web
controls render HTML 4.0-compliant markup for modern, non-Microsoft browsers.

While ASP.NET can be easily configured to render HTML 4.0-compliant markup for additional browsers, one thing
that this technique does not solve for is that of client-side validation with the validation controls. ASP.NET
provides a number of validation controls -
RequiredFieldValidator, CompareValidator, and so on - that are useful for ensuring that a user enters valid values
into form fields. With Internet Explorer, these controls emit client-side JavaScript that perform the validation on
the client-side in addition to the server-side. For non-Microsoft browsers, only server-side validation is possible.
Unfortunately, tweaking ASP.NET to get the validators to use client-side validation for non-Microsoft browsers is
not as easy as one would hope. This article examines why and presents a workaround.

How the Validation Controls Render Client-Side Script

All of the ASP.NET validation controls are derived from the System.Web.UI.WebControls.BaseValidator class.
The BaseValidator class defines the methods and properties common to all validation controls. One
of the things the BaseValidator class does is determine whether or not client-side script should be
rendered. This is accomplished in the PreRender stage by the class's DetermineRenderUplevel() method.
This method indicates that client-side scripting should be emitted if the following three conditions hold true:

The validator's EnableClientScript property is True (the default)

The requesting browser's MSDomVersion property is version 4.0 or higher.

The requesting browser's EcmaScriptVersion property is version 1.2 or higher.

MSDomVersion and EcmaScriptVersion are properties of the Browser object.
As discussed in A Look at ASP.NET's Adaptive Rendering, the Browser
object contains read-only properties that describe the capabilities of the requesting browser. These capabilities
are determined by the <browserCaps> settings in the maching.config file or Web.config file.

Given this, you might think that enabling client-side validation in modern, non-Microsoft browsers would be as simple
as tweaking the maching.config file, setting the MSDomVersion to 4.0 or greater for
the appropriate User-Agent string for these browsers. This will, indeed, cause the client-side script for the validation
controls to be emitted, but, sadly, things will still not work.

The reason modern, non-Microsoft browsers do not support client-side validation in the validation controls is because
of the client-side script emitted by BaseValidator. The client-side validation script emitted by BaseValidator
includes, among other things, a JavaScript block that creates an array of the validators. This array, then, can be
enumerated on the client-side script to check the validity of all of the validation controls. This array is registered
in the BaseValidator's RegisterValidatorDeclaration() method, but unfortunately uses
the client-side script document.all[validatorID]. An example of the array declaration when rendered
on an ASP.NET Web page can be seen below:

The problem with document.all[ID] is that Microsoft's Internet Explorer is the only browser that
supports this non-standard technique for referencing an element in the HTML document. The standard way (which Internet
Explorer does support, along with Mozilla, Netscape, Opera, and others), is: document.getElementById(ID).
So, the script emitted by the BaseValidator class will do nothing but generate JavaScript errors in non-Microsoft
browsers. Bummer.

On top of the document.all snafu, there are some minor issues in the WebUIValidation.js JavaScript
file ASP.NET uses to perform validation. For example, Mozilla doesn't like the use of the variable name final (on lines
341, 349, 356, and 369). These issues, though, can be easily fixed since WebUIValidation.js
can be modified with any ol' text editor. Fixing the Internet Explorer-only document.all emitted by
BaseValidator requires much more work, as we'll soon see.

In order to fix the client-side validation for non-Microsoft browsers, we need to replace the document.all[ID]
portion in the BaseValidator class with a standards-compliant document.getElementById(ID).
There are a few ways to do this.

Create a new class that derives from BaseValidator. This class could then override the
RegisterValidatorDeclaration() method and emit the proper script. Next, you would have to create a new
class for each of the validation controls (RequiredFieldValidator, CompareValidator, and so on), having these derive
from the extended BaseValidator class.

Build everything from scratch. This entails building a BaseValidator-like class
from the ground up, followed by creating the validation controls derived from the new BaseValidator-like class.

Delegate this to someone else. This is my favorite choice for most things! :-) For instance, you can
use Paul Glavich's free DomValidators
controls. These controls are build from the ground up, and emit only standards-compliant script. The DomValidators
include the complete source code and instructions on how to start using the DomValidators in an ASP.NET application.

There are also third-party commercial components available. For example, Peter Blum
offers a set of validation controls he calls Professional Validation and
More. Like Paul Glavich's DomValidators, Peter's control library was written from the ground up. In addition to
providing the five base validation controls, Peter's product includes an additional 17 more validation controls, such
as WordCountValidator, CreditCardNumberValidator, and others. There are a bevy of other features worth checking out
as well.

As Peter and Paul and shown, having suitable client-side script emitted for modern, non-Microsoft browsers is indeed
possible, but not as simple as it should be. Of course, all of this will be taken care of with ASP.NET 2.0, but we
still live in a 1.x world.

Conclusion

The default ASP.NET validation controls do not provide a working client-side script model for modern, non-Microsoft browsers
due to the fact that proprietary script - document.all[ID] - is used in place of standards-compliant
script - document.getElementById(ID). Client-side support can be added, but at the cost of
recreating the validation controls. Fortunately, the work has already been provided by Paul Glavich,
so you can use his DomValidators
validation controls if you are needing to support client-side validation with non-Microsoft browsers.

By applying the techniques discussed in this article along with the techniques mentioned in
A Look at ASP.NET's Adaptive Rendering, you can make ASP.NET render identically for
both Internet Explorer and equally capable non-Microsoft Web browsers.