Navigate to Ken's Corner Home Page Frequently Asked ASP.NET Questions Classic Hits Game Written in ASP.NET Send Email to Ken Ken's Resume (available for contracts!) Microsoft Most Valuable Professional Award Ken's Corner of ASPAlliance

FAQ 0006

Michael W. Moore deals with a number of issues related to client-side scripting in .aspx pages:

  • The validation controls stole my client-side click event. How do I get it back?
  • How do I display a confirmation dialog before submitting a form?
  • How do I control the sequence in which validation controls run on the client?
  • If I use attachEvent to add MULTIPLE functions to a click event, how do I control the sequence in which they run?

Part 1 Client click code may not be necessary

 Disable individual validators

 Custom validators are very flexible

 Different validation for different buttons

 Pre-submission processing

Part 2 The client click event

 attachEvent

 attachEvent documentation

 Event specific script block

 Single custom validator

 Modify the existing function

 CausesValidation = False

 HTML button

 Assign a new function to the event

Part 3 Controlling the sequence of events

 Validator sequence

 attachEvent sequence

Part 4 References

 General Validation Info

 In - Depth

 More References

There are several little known functions which may meet your needs better than adding a client click event. Plus, there are several methods to add code to the click event. I'm leaning towards attachEvent as my favorite.

This may not be the final word on this subject, but below is my opinion. Responses and critiques are welcome in news://msnews.microsoft.com/microsoft.public.dotnet.framework.aspnet.

Part 1 Client click code may not be necessary

A client-click event may not be necessary. Here are some methods which may make it unneeded.

Disable individual validators

Suppose you only want to require text in a text box if a check box is checked. Start with a RequiredFieldValidator that is enabled. In your client onload event, disable it and set the check box to UNchecked. Then add code to the check box's onchange event to enable/disable the RequiredFieldValidator. Call ValidatorEnable and pass it the validator object [document.all("NameOfValidator")], and true or false.

ValidatorEnable(validator, boolean)

Custom validators are very flexible

If you leave a custom validator's "ControlToValidate" blank, then it will always run with every form submission. Currently, it will NOT run if it's set to a blank text box. If not set to any control, then it always runs. Add whatever code you want (both client and server code) to have it test anything in the document.

Different validation for different buttons

Suppose you have two (or more) sections in your form. Before ASP.NET, you could put each in its own form and submit them separately. Now, we only have one form. You can still control validation so that it skips the controls in the other sections.

There are several ways to do this. One is to have only one section active at a time. You could add buttons which allow the user to enable the section they want to submit. This button would be a HTML button which does NOT post the form. It would just call client code to enable & disable the various controls and validators -- ValidatorEnable(validator, boolean).

Another, very similar, way to do this is to have two buttons, in each section, to submit the section. One would be named "Validate" and the other "Submit". "Submit" would be disabled until the Validate button is clicked & validation passes correctly. The Validate button would be an HTML button that runs client code. It would first call Page_ClientValidate and then enable the Submit button if Page_IsValid is true.

Another way is to use an HTML button to fire a client function. In this function, call Page_ClientValidate and then, if Page_IsValid = true, call Form1.submit(), or __doPostBack(...). Note that you will need to add SERVER code to execute server-side validation -- Page.Validate().

Another way is via pre-submission processing. In the OnSubmit() function, use event.srcElement to determine which button was clicked (for Navigator it's event.target, though client validation is normally off for Navigator). [As shown below, you can also do this in the Submit() function.]

Pre-submission processing

Some objects, such as web server buttons, actually end up on the client browser as "submit" buttons. These controls will run validation, then call OnSubmit(), and then post to the server. Other objects, such as links, render to the client browser as calls to __doPostBack(...). These call Submit() and then post to the server. All HTML objects which post a form either execute the onsubmit() function or the submit() function. Therefore, adding your own code to one or both of these allows you to add your own code to the submission process.

You can easily add your own code to the onsubmit function. Using RegisterOnSubmitStatement in server code is the recommended method. If you know that the click event you need is one that triggers OnSubmit(), then adding code to OnSubmit() could be solution for you.

The submit() function is harder. The submit() function is compiled binary code rather than a string. The method described below, "Assign a new function to the event", is probably the only way to "add" your own code to this function. However, the "assign new function" method can make it harder for programmers to read or understand the page later. Also, if there are multiple overrides of the function, then it would be even harder to understand. Plus, if multiple overrides use the same variables, then the second use of the same variable will erase its previous contents and that will cause your page to never submit.

Part 2 The client click event

attachEvent

This method allows you to add one (or more) client function calls to your event. The button's standard click event will run first (so validation will run first). Then your added functions will run. If you attach multiple functions, they may run in any order.

Thanks to Mark O'Sullivan for finding this method.

  • Your functions run after the INITIAL run of the validation controls. That does not prevent you from calling validation again. You can use your code to rerun individual validators, make modifications (such as disable some validators), and run Page_ClientValidate again.
  • This only works in IE5 or later. I haven't tested Navigator.
  • Server-side validation is unaffected, which is probably what you want. If you also want to modify server validation, then declare a new Validate function in the code-behind -- public override void Validate() { //your code here }. You'll probably want to call base.Validate() somewhere within your override Validate function.

attachEvent documentation

http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/attachevent.asp

One ambiguous note - the documentation says that detachEvent must also be called. However, only the *.HTC sample uses detachEvent. The HTML sample does not use it.

To use this method

The following is JavaScript. For VBScript, use GetRef("btn_onclick").

<PUBLIC:ATTACH ONEVENT="cleanup()" EVENT="ondetach" />

<script language="javascript">

function btn_onclick() {

return confirm('Do this?');

}

function cleanup() {

document.all("Button1").detachEvent("onclick", btn_onclick);

}

function window_onload() {

document.all("Button1").attachEvent("onclick", btn_onclick);

}

</script>

Event specific script block

This solution is nearly identical to the "CausesValidation=false" method, except:

  • It uses a script block with for="Button1" event="onclick".
  • It does not have a return value. Instead, it uses IE's "returnValue property of the event object.
  • The validation code fails to fire, even if CausesValidation is set to True.

This is normal. If you put an ordinary HTML button on a form and add both an onclick attribute inside the tag plus an onclick event script bock, then the script block will fire and the attribute will not fire. This is why I put this as the second solution. With this solution (with CausesValidation=True), it is confusing if you use View Source in your browser. You will see TWO onclick events. One inside the button tag, calling validation code. And, another in a separate event script block. It can lead to confusion to see both of these while only the script block code gets executed.

Downside:

  • More complex to read as the control's tag no longer summarizes what events are being fired. You must read all the script blocks & cross reference them to the controls in order to follow execution.
  • Future changes to server controls could add new functionality to web server button control onclick events. This method could prevent those newer functions from running, just as it currently prevents any onclick= code that is inside the tag.
  • I have not tested this with Navigator. My documentation says its "event" object does not have an equivalent to IE's "returnValue" property. Somehow, this script block must be able to prevent form submission.

<script for="Button1" event="onclick" >

if (typeof(Page_ClientValidate) == 'function')

{

Page_ClientValidate();

event.returnValue = Page_IsValid;

}

else

{

event.returnValue = true;

}

</script>

Single custom validator

If you only have one validation control, and it's a custom validator, then you can use its client validation function as your click event in addition to using it to validate your page. If you use more than one validator, then it may be difficult to control the sequence in which each validator is executed. Leave the custom validator's "ControlToValidate" property blank, then it always runs. Also, see below for: "Validate differently depending on which submit button was clicked"

Modify the existing function

In JavaScript, a function is essentially a string. You can use string functions to manipulate the contents of the function. First find the first curly brace and strip it off, along with everything before it. Then strip off the closing curly brace. Now you have just the code. You can add more code to the beginning or end of this string. If you add to the beginning, be sure to end your additional code with a semi-colon and a space. If you add to the end, then you need to do some extra work. Since many people do not add semi-colons at the end of their lines of code, you need to calculate, in your code, whether you need to add a semi-colon prior to your additional code.

Note: Though there is nothing inherently wrong with this method, you are modifying the code inside another function. This could lead to unexpected results, especially if multiple client scripts are each modifying the same function. The validation controls already use this method on the onclick/onchange events of controls that are being validated. Also, some third party controls use this method. Caution is suggested.

Here is a sample of this method. This sample is from WebUIValidation.js version 1_0_3705_0.

var ev;

if (control.type == "radio") {

ev = control.onclick;

} else {

ev = control.onchange;

}

if (typeof(ev) == "function" ) {

ev = ev.toString();

ev = ev.substring(ev.indexOf("{") + 1, ev.lastIndexOf("}"));

}

else {

ev = "";

}

var func = new Function("ValidatorOnChange(); " + ev);

if (control.type == "radio") {

control.onclick = func;

} else {

control.onchange = func;

}

CausesValidation = False

With this method, validation does not execute on its own. Your code has complete control over calling validation.

Compatibility: ASP.NET client validation is functional with IE4 or greater. attachEvent only works with IE5 or later.

This method allows you to customize server validation as well as client. Just add your custom validation code to the server code. Then you can duplicate the custom logic on both the client and the server.

Problem: Though this method treats client & server equally (validation is turned off in both), the original problem was only a client-side problem. Turning off server validation is probably not wanted, though you can add server code to call Page.Validate(). If you do add a server-side call to Page.Validate(), you will most closely align your usage of it to the original if you add it as the first line of code in the server click event for the button that the end user clicked. Another option is to add it to the server-side page_load event.

To use this method

Set the button's CausesValidation property to false.

Add this to code-behind page_load

Button1.Attributes.Add("language", "javascript")

Button1.Attributes.Add("OnClick", "Button1_onclick();")

Add client code for the button, such as the following

function Button1_onclick()

{

Var MyResult;

//Your code here, before validation

if (typeof(Page_ClientValidate) == 'function')

{

Page_ClientValidate();

MyResult = Page_IsValid;

}

else

{

MyResult = true;

}

//More of your code here, after validation

return MyResult;

}

HTML button

Replace the web server button control with an HTML button with the following added:

Add: id="..." name="..." onclick="…();" runat=server

Add: an event in the code-behind for this button's ServerClick event (otherwise it won't submit the form). This server-side procedure may be empty.

Add: a client event for the control & add a call to validation within that procedure.

The function should return true or false to allow or stop form submission.

This control does not have a CausesValidation property. Therefore, its client-side click event should behave just like one would expect from an HTML button.

Downside: It's not a web server control. It won't benefit from future enhancements such as newer or other controls adding functionality to buttons.

Problem: Though this method treats client & server equally (validation is turned off in both), the original problem was only a client-side problem. Turning off server validation is probably not wanted, though you can add server code to call Page.Validate(). If you do add a server-side call to Page.Validate(), you will most closely align your usage of it to the original if you add it as the first line of code in the server click event for the button that the end user clicked. Another option is to add it to the server-side page_load event.

To use this method

function Button1_onclick()

{

if (typeof(Page_ClientValidate) == 'function')

{

Page_ClientValidate();

return Page_IsValid;

}

else

{

return true;

}

}

Assign a new function to the event

The idea here is to use a page level variable and code in the page_onload event to supercede the button's onclick procedure with a new procedure of your own. Within your own procedure, you also use the page level variable to call the original procedure. Thus, both your code and the original code get executed.

Caution: This results in code that jumps around so much that it may not be feasible to maintain. It's even harder to follow than the old GoTo statement. This problem makes this method impractical to use.

Caution: If there are multiple overrides of the function, then it would be even harder to understand. Plus, if multiple overrides use the same variables, then the second use of the same variable will erase its previous contents and that will cause the code from the original procedure to be lost.

The following example shows overriding the submit() function. The method is the same for any function.

var OldSubmit;

function NewSubmit() {

alert("test");

OldSubmit();

}

function window_onload() {

OldSubmit = Form1.submit;

Form1.submit = NewSubmit;

}

Part 3 Controlling the sequence of events

Currently, there are two things that make you think you don't have control:

  • Validator sequence: You don't much have control over the order in which Page_ClientValidate executes the various validators.
  • attachEvent sequence: If you use attachEvent multiple times for the same event, you don't have control over the order in which the attached functions execute.

You can regain control of these as follows.

Validator sequence

Even if the validation has already run before your client function runs (such as your click event code), you can still run validation again. In your code, you can run the validators in any sequence you wish. You can hard code the names of the validators or use the array (Page_Validators).

Perhaps there's a validated field that you might be able to "fix" via code. You can run that validator, if false, run your code to attempt to fix the error, and then run it again. You can also disable unwanted validators via ValidatorEnable(validator, boolean). You can override a control's results by setting that control's isvalid (lower case) property to true/false. You can also set the page variable Page_IsValid to true/false. If you rerun any validators, or otherwise change the isvalid results of one or more validators, then you need to update the page variable Page_IsValid. You can do this by calling ValidatorUpdateIsValid(). It loops through the array of validators and updates Page_IsValid.

attachEvent sequence

Don't use attachEvent more than once per event. If you want to add more to an event, use attachEvent once and then do something to add more code to the one function you already added to the event. Perhaps you might add logic to the one event so it decides which other functions to call. Perhaps you add additional functions to an array and then have the one function call all the functions in the array. Another option is to build a string containing all the code that you want to run. While assembling the string, add the code in the desired sequence. Finally, turn that code into a function with the JavaScript Function command. Then you can add it with attachEvent. Techniques like these allow you to write code which will execute in the sequence you want.

Array of functions:

//Add the function to the array

//No parentheses after MyFunction

MyFunction = Button1_onclick;

arr(1) = MyFunction;

//Run the function

MyFunction = arr(1);

MyFunction();

Part 4 References

General Validation Info

User Input Validation in ASP.NET

http://msdn.microsoft.com/library/en-us/dnaspp/html/pdc_userinput.asp

Q316662 HOW TO: Use ASP.NET Validation Controls from Visual Studio .NET

http://support.microsoft.com/support/kb/articles/q316/6/62.asp

Server Control Form Validation

http://samples.gotdotnet.com/quickstart/aspplus/doc/webvalidation.aspx

QuickStart tutorial

http://msdn.microsoft.com/library/en-us/cpqstart/html/cpsmpnetsamples-aspnetservercontrolformvalidation.asp

In - Depth

* ASP.NET Validation in Depth

(includes how to selectively enable/disable validators on the server or on the client)

http://msdn.microsoft.com/library/en-us/dnaspp/html/aspplusvalid.asp

More References

Validation with Navigator and Opera

http://msdn.microsoft.com/library/en-us/cpguide/html/cpconvalidatorcontrolsamples.asp

attachEvent

http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/attachevent.asp

Regular expressions

http://www.effectiveperl.com/EP.04.pdf

http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=sarMgCxQCHA.2348%40cpmsftngxa06

http://groups.google.com/groups?selm=E9Is4t.Arr.0.sheppard%40torfree.net&oe=UTF-8&output=gplain

http://www.perldoc.com/perl5.6/pod/perlfaq6.html

Validating blank fields and interrelated fields

http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=6JgO0CQPCHA.2372%40cpmsftngxa07

Validate differently depending on which submit button was clicked

[My reference for this is out-of-date. So, here's the info.]

1) You need some client code to execute. You can use this article to add a click event, or you can add a custom validation control (with its ControlToValidate left blank).

2) If your client code is via adding an onclick event, then you know which button was clicked. If your client code is via a custom validator, then use event.srcElement.name or document.all(event.srcElement).name. For Navigator clients, my documentation says to use "target" instead of "srcElement".

Use one custom validation control for a VERY large number of controls (client-side sample code only)

http://groups.google.com/groups?safe=images&ie=UTF-8&oe=UTF-8&as_umsgid=jr1JgvhVCHA.2960@cpmsftngxa10&lr=&hl=en

4) Enable & Disable Individual Validators

I'm repeating this since it gets asked so much.

You can enable/disable individual validation controls in either CLIENT code or in SERVER code.

http://msdn.microsoft.com/library/en-us/dnaspp/html/aspplusvalid.asp

Sample code extracted from the above link:

Client:

function OnChangeSameAs() {

var enableShip = !event.srcElement.status;

ValidatorEnable(rfvalShipAddress, enableShip);

}

Server:

public override void Validate() {

// Only check ship address if not same as billing

bool enableShip = !chkSameAs.Checked;

rfvalShipAddress.Enabled = enableShip;

// Now perform validation

base.Validate();

}

This posting is provided "AS IS", with no warranties, and confers no rights.