I started by adding validation groups (as per Dave Ward's blog post) so I had two logical groups. After a VERY long look into the JQuery Validate documentation and source code I narrowed the investigation down to a single function: showErrors(). This gets called each time before any error is potentially displayed whether it is on the form submission event or a blur event of one of the elements. By changing the settings accordingly this ensures the correct display settings are always used for the right element.
In the code below one validation group is set to display errors in a UL list summary and the other inline and with a different css class. I've extended the showErrors() function to dynamically switch the error settings based on which validation group the element that has an error is contained in. You could probably take this further and bind the settings to the validation container to avoid the clunky IF statement, but I've used the simple version below as it better illustrates the solution. Finally I call the defaultShowErrors() which as one would expect calls the default function in the validate framework.
$("#aspForm").validate({
onsubmit: false,
// This prevents validation from running on every
// form submission by default.
// Extend the show errors function
showErrors: function (errorMap, errorList) {
// here we get the element linked to the error.
// we then find out which validation group the element in question
// belongs to and set the correct properties
if (errorList[0]) {
var element = errorList[0].element;
// at the time of calling we configure the settings for the validate form
if ($(element).parents('.validationGroup').attr("id") == "signup") {
this.settings.errorClass = "errorSignUp";
this.settings.errorContainer = $("*[id$='uivalidation']");
this.settings.errorLabelContainer = $("*[id$='uivalidation'] ul");
this.settings.errorElement = "li";
} else {
// these are the defaults
this.settings.errorClass = "error";
this.settings.errorContainer = $([]);
this.settings.errorLabelContainer = $([]);
this.settings.errorElement = "label";
}
}
// call the default show errors function after we have hooked up the correct settings
this.defaultShowErrors();
}
});
This does exactly what I was looking for since it means I do not have to make any changes to the validate framework. This is demonstrated in the full working example below where I am using a CDN for JQuery and JQuery.Validate!
Full Code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Multiple Form Validation</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.9/jquery.validate.min.js"></script>
<style type="text/css">
*
{
font-family: Verdana;
font-size: 96%;
}
label
{
width: 10em;
float: left;
}
label.errorLogin
{
float: none;
color: blue;
padding-left: .5em;
vertical-align: top;
}
label.error
{
float: none;
color: red;
padding-left: .5em;
vertical-align: top;
}
p
{
clear: both;
}
.submit
{
margin-left: 12em;
}
em
{
font-weight: bold;
padding-right: 1em;
vertical-align: top;
}
</style>
<script type="text/javascript">
$(function () {
$("#aspForm").validate({
onsubmit: false,
// This prevents validation from running on every
// form submission by default.
// Extend the show errors function
showErrors: function (errorMap, errorList) {
// here we get the element linked to the error.
// we then find out which validation group the element in question
// belongs to and set the correct properties
if (errorList[0]) {
var element = errorList[0].element;
// at the time of calling we configure the settings for the validate form
if ($(element).parents('.validationGroup').attr("id") == "signup") {
this.settings.errorClass = "errorSignUp";
this.settings.errorContainer = $("*[id$='uivalidation']");
this.settings.errorLabelContainer = $("*[id$='uivalidation'] ul");
this.settings.errorElement = "li";
} else {
// these are the defaults
this.settings.errorClass = "error";
this.settings.errorContainer = $([]);
this.settings.errorLabelContainer = $([]);
this.settings.errorElement = "label";
}
}
// call the default show errors function after we have hooked up the correct settings
this.defaultShowErrors();
}
});
// Search for controls marked with the causesValidation flag
// that are contained anywhere within elements marked as
// validationGroups, and wire their click event up.
$('.validationGroup .login').click(ValidateAndSubmit);
$('.validationGroup .signup').click(ValidateAndSubmit);
// Select any input[type=text] elements within a validation group
// and attach keydown handlers to all of them.
$('.validationGroup :text').keydown(function (evt) {
// Only execute validation if the key pressed was enter.
if (evt.keyCode == 13) {
ValidateAndSubmit(evt);
}
});
});
function ValidateAndSubmit(evt) {
// Ascend from the button that triggered this click event
// until we find a container element flagged with
// .validationGroup and store a reference to that element.
var $group = $(evt.currentTarget).parents('.validationGroup');
var isValid = true;
// Descending from that .validationGroup element, find any input
// elements within it, iterate over them, and run validation on
// each of them.
$group.find(':input').each(function (i, item) {
if (!$(item).valid())
isValid = false;
});
// If any fields failed validation, prevent the button's click
// event from triggering form submission.
if (!isValid)
evt.preventDefault();
}
</script>
</head>
<body>
<form id="aspForm" runat="server">
<fieldset class="validationGroup" id="login">
<div id="uivalidation">
<ul></ul>
</div>
<legend>Register</legend>
<p>
<asp:Label ID="uiFirstName" runat="server" AssociatedControlID="uxFirstName" Text="First name:"></asp:Label>
<asp:TextBox ID="uxFirstName" runat="server" CssClass="required"></asp:TextBox>
</p>
<p>
<asp:Button ID="uxRegister" runat="server" Text="Register" CssClass="login" />
</p>
</fieldset>
<fieldset class="validationGroup" id="signup">
<legend>Login</legend>
<p>
<asp:Label ID="uiUserName" runat="server" AssociatedControlID="uxUserName" Text="User name:"></asp:Label>
<asp:TextBox ID="uxUserName" runat="server" CssClass="required"></asp:TextBox>
</p>
<p>
<asp:Button ID="uxLogin" runat="server" Text="Login" CssClass="signup" />
</p>
</fieldset>
</form>
</body>
</html>
If this could be further improved please jump in and edit the code.