7

I am using Twitter's Bootstrap as my framework for a project I am working on. I am attempting to validate a simple contact form using the jQuery Validate plugin but it's causing some headaches.

I'm looking to add an Error or Valid class to the parent div of each form's input. Here's my form code:

 <form id="msg-form" class="">
    <fieldset>
        <div class="input-prepend control-group pull-left">
            <span class="add-on">@</span>
            <input class="span3 required email" id="inputEmail" name="msg-email" type="text" placeholder="example@example.com">
        </div>
        <div class="input-prepend control-group pull-left">
            <span class="add-on"><i class="icon-user"></i></span>
            <input class="span3 required" id="inputName" name="msg-name" type="text" placeholder="Joe Bloggs">
        </div>
        <div class="input-prepend control-group">
            <span class="add-on" style="height: 53px;"><i class="icon-pencil"></i></span>
            <textarea class="span3 required" id="textarea" name="msg-comments" rows="2" style="height: 53px" placeholder="Just say hi, or tell us how we can help"></textarea>
        </div>
        <button type="submit" class="btn pull-right">Submit<i class="icon-chevron-right"></i></button>
    </fieldset>
</form> 

As you can see my div.control-group is the div I want to add the class to. I have attempted to use the highlight (as suggested here, but this simply didn't work) & errorPlacement (see below) but I'm not doing it correctly.

Ideally I want the class to be added/removed on keyup so it keeps the great functionality that the plugin offers. This simple code works, but it does not remove the class & only works on submission of the form:

$("#msg-form").validate({
    errorPlacement: function(error, element) {
        $(element).parent('div').addClass('error');
    }
});

Can anyone suggest what option I should be using to achieve this?!

Thanks in advance - I can provide any additional details in necessary!

Community
  • 1
  • 1
Sheixt
  • 2,546
  • 12
  • 36
  • 65
  • it looks like it should be `$(error).parent('div').addClass('error');` let me know if it did the trick – gdoron May 03 '12 at 13:06
  • @gdoron thanks for the suggestion, sadly it didn't work. The code I posted above, causes the class to be applied, but has two faults: 1.) It does not remove the class when field is then valid 2.) It only functions for the errors (& ideally I would like to add an alternative class for valid inputs) – Sheixt May 03 '12 at 13:10
  • possible duplicate of [I want jQuery validator to add a class to the form element's parent](http://stackoverflow.com/questions/3067604/i-want-jquery-validator-to-add-a-class-to-the-form-elements-parent) – KyleMit Jan 26 '15 at 19:36

3 Answers3

13

Try using the highlight and unhighlight functions:

$("#msg-form").validate({
    highlight: function(element) {
        $(element).parent('div').addClass('error');
    },
    unhighlight: function(element) {
        $(element).parent('div').removeClass('error');
    }
});
user1418227
  • 201
  • 1
  • 4
  • 12
James Simm
  • 1,569
  • 1
  • 16
  • 28
  • Thank you for the suggestion. As it goes I had already tried this approach. I have inserted the code as suggested & nothing functions... The form validation doesn't even appear to fire!! I've double checked Firebug and no errors are presented. I'm using v1.9.0 of the plugin & v1.7.1 of jQuery if that's of any help. – Sheixt May 03 '12 at 13:48
  • I think your ` – James Simm May 03 '12 at 13:53
  • Didn't think of that! Alas, still no joy!! The element type doesn't appear to make a difference. Come to think of it, if that was the cause of the problem, wouldn't even the basic validation have failed to fire?! Annnnnyyyywwaaaayy... this is a weird one. I can't think of any (obvious) reason why this wouldn't work. – Sheixt May 03 '12 at 14:10
  • Out of interest, are you using IE? I've had the validation pickup the placeholder as the value in the past, and therefore not fire the "required" validator. – James Simm May 03 '12 at 15:58
  • Nope, I'm using Firefox as the main browser I'm building the project in. Afterwards I will do cross-browser testing.... I have attempted removing the placeholder references but this still doesn't submit (with the highlight approach). – Sheixt May 04 '12 at 13:27
  • Bizarre, I'm doing exactly the same thing in a number of my applications and highlight and unhighlight accomplish this perfectly. Sorry I couldn't be of more help :) – James Simm May 08 '12 at 13:09
  • Hmmm.... maybe it's versioning thing... may I ask what versions you are using? I'm also running through my code to ensure I don't have any silly little errors. In any event, thank you for all your help James. – Sheixt May 09 '12 at 12:16
  • Actually, please ignore that previous comment! I think I am getting somewhere. They highlight does appear to work I define the errorElement: eg `$("#msg-form").validate({ errorElement: "nothing", highlight: function(element, errorClass) { $(element).parent('div').addClass('error'); }, unhighlight: function(element, errorClass) { $(element).parent('div').removeClass('error'); } });` I shall confirm once I have a concrete solution – Sheixt May 09 '12 at 12:31
4

From my answer on add class to parent div if validation error occurs using jquery validation because I believe it adds value to the existing answers on this question ...

Access Validator Settings

The first order of business is to modify the settings object on your form's validator. You can do this in any of the following ways:

  1. Before the form is loaded for all forms by calling jQuery.validator.setDefaults()
  2. When initializing the form by passing in options on .validate([options])
  3. After initialization by locating the validator object on the form with $("form").data("validator").settings

Since you're using MVC, option #2 is out of the question since unobtrusive-validation will automatically initialize the form. So let's use option 3 going forward - the goal here is just to be able to customize the settings on the form.

Override Default Behavior

The default methods we'll want to modify are highlight and unhighlight which will highlight invalid fields or revert changes made by the highlight option, respectively. Here's their default behavior according to the source code:

highlight: function( element, errorClass, validClass ) {
    if ( element.type === "radio" ) {
        this.findByName( element.name ).addClass( errorClass ).removeClass( validClass );
    } else {
        $( element ).addClass( errorClass ).removeClass( validClass );
    }
},
unhighlight: function( element, errorClass, validClass ) {
    if ( element.type === "radio" ) {
        this.findByName( element.name ).removeClass( errorClass ).addClass( validClass );
    } else {
        $( element ).removeClass( errorClass ).addClass( validClass );
    }
}

So you have a couple options here as well.

  1. Completely replace those functions and write them on your own
  2. Wrap those functions and call them like normal but add your own custom code before or after.

Option 1 - Replace Wholesale

This route is pretty easy. Just write whatever you want in there. Maybe seed from the source code, maybe do your own thing.

var valSettings = $("form").data("validator").settings
valSettings.highlight = function(element, errorClass, validClass) { ... }
valSettings.unhighlight = function(element, errorClass, validClass) { ... }

Option 2 - Wrap Functions

This is less intrusive so probably preferable in most cases.

Since ultimately you will be replacing the value of valSettings.highlight, you'll need access to a clean pristine version of the original function. You can save your own or grab one off the global defaults

// original highlight function
var highlightOriginal = $("form").data("validator").settings.highlight;
var highlightDefaults = $.validator.defaults.highlight

In terms of wrapping JavaScript functions, there are a couple examples here, here, and here). Here's a parsed down example from one of those, that will help bind this context across function calls, preserve the arity of the arguments being passed, and persist the return value:

function wrap(functionToWrap, beforeFunction) {
    return function () {
        var args = Array.prototype.slice.call(arguments),
        beforeFunction.apply(this, args);
        return functionToWrap.apply(this, args);
    };
};

Then you'll also have to quickly define whatever additional behavior you want to fire whenever the call is made. In this case, let's find the closest parent div to the element and update it's classes like this:

function highlightDecorator(element, errorClass, validClass) {
    $(element).closest("div").addClass(errorClass).removeClass(validClass);
}

Wrapping It All Up (see what I did there)

$(function () {
  var valSettings = $("form").data("validator").settings
  valSettings.highlight = wrap($.validator.defaults.highlight, highlightDecorator)
  valSettings.unhighlight = wrap($.validator.defaults.unhighlight, unhighlightDecorator)
});

function wrap(functionToWrap, beforeFunction) {
  return function () {
    var args = Array.prototype.slice.call(arguments);
    beforeFunction.apply(this, args);
    return functionToWrap.apply(this, args);
  };
};

function highlightDecorator(element, errorClass, validClass) {
  $(element).closest("div").addClass(errorClass).removeClass(validClass);
}
function unhighlightDecorator(element, errorClass, validClass) {
  $(element).closest("div").addClass(validClass).removeClass(errorClass);
}

So when we combine all of the above functions, it should look something like this:

Working Demo in Stack Snippets and jsFiddle

$(function () {
  var valSettings = $("form").data("validator").settings
 valSettings.highlight = wrap($.validator.defaults.highlight, highlightDecorator)
  valSettings.unhighlight = wrap($.validator.defaults.unhighlight, unhighlightDecorator)
});

function wrap(functionToWrap, beforeFunction) {
  return function () {
    var args = Array.prototype.slice.call(arguments);
    beforeFunction.apply(this, args);
    return functionToWrap.apply(this, args);
  };
};

function highlightDecorator(element, errorClass, validClass) {
  $(element).closest("div").addClass(errorClass).removeClass(validClass);
}
function unhighlightDecorator(element, errorClass, validClass) {
  $(element).closest("div").addClass(validClass).removeClass(errorClass);
}
input.input-validation-error  {
  border: solid 1px red;
}
.input-validation-error label {
  color: red;
}
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.16.0/jquery.validate.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js"></script>
    
<form action="/Person" method="post">
  
  <div class="required">
    <label for="Name">Name <em>*</em></label>           
    <input id="Name" name="Name" type="text" value="" 
           data-val="true" data-val-required="The Name field is required."/>
    <span class="field-validation-valid" 
          data-valmsg-for="Name" data-valmsg-replace="true"></span>
  </div>
  
  <input type="submit" value="Save" />
  
</form>
KyleMit
  • 30,350
  • 66
  • 462
  • 664
0

I might be wrong but it looks like you need .parents('div.control-group') not .parent(), which adds .error to the wrong div not changing BootStraps form colors to red.

This is because the input that JQuery Validate is trying to highlight is two below the .control-group that BootStrap requires for .error to take its effect.

$("#form").validate({
    highlight: function(element, errorClass, validClass){
    $(element).parents("div.control-group").addClass(errorClass).removeClass(validClass);
},

Hope this helps

BPWDevelopment
  • 919
  • 1
  • 7
  • 8