Question

I am having a really strange problem with Jquery validate that only occurs in Chrome. The validation on this page seems to be firing both the Highlight and the Unhighlight functions in the .validate() function so if I dont fill out the form it cycles through each element and applies the "invalid" class in the highlight function but then for some reason it goes through and immediately applies the code in unhighlight and I cant work out why?

JS

$(document).ready(function () {
    //Validation for form fields on Payment form

    /*This adds a method to test whether value is equal to placeholder, and if it is, don't consider it filled out.  This is necessary to circumvent IE placeholder plugin*/
    jQuery.validator.addMethod("notEqual", function (value, element, param) {
        return this.optional(element) || value != param;
    }, "Required.");

    $('#payment-form').validate({
        onfocusout: function (element) {
            $(element).valid();
        },
        rules: {
            "data[Payment][card_holder]": { required: true, minlength: 2 },
            "data[Payment][card_number]": { required: true, creditcard: true },
            "data[User][first_name]": { required: true, notEqual: "First Name" },
            "data[User][last_name]": { required: true, notEqual: "Last Name" },
            "data[UserDetail][company]": { required: true },
            "data[UserDetail][job_title]": { required: true },
            "data[UserDetail][telephone]": { required: true },
            "data[User][email]": {
                required: true,
                email: true,
                remote: {
                    url: "/usermgmt/users/email_exists",
                    type: "post"
                }
            },
            "data[User][password]": { required: true },
            "data[Address][billing_line_1]": { required: true },
            "data[Address][billing_line_2]": { required: true },
            "data[Address][billing_state]": { required: true },
            "data[Address][billing_postcode]": { required: true },
            credit_exp_month: { required: true, notEqual: "MM", number: true, max: 12, minlength: 2, maxlength: 2 },
            credit_exp_year: { required: true, notEqual: "YYYY", number: true, minlength: 2, maxlength: 4 },
            "data[Payment][cvv]": { required: true, number: true, minlength: 3, maxlength: 4 },
        },
        errorClass: 'error',
        unhighlight: function (element, errorClass, validClass) {
            $(element).removeClass(errorClass).addClass(validClass);
            validateIcon(element);
        },
        highlight: function (element, errorClass, validClass) {
            $(element).addClass(errorClass).removeClass(validClass);
            validateIcon(element);
        }
    });

    function validateIcon(element) {
        $(element).siblings('span.validate_icon').remove();
        if ($(element).hasClass('error')) {
            alert("error");
            $(element).closest('li').find('label>span:first').html('<span class="validate_icon invalid"> <span class="icon-stack"><i class="icon-sign-blank icon-stack-base"></i><i class="icon-exclamation"></i></span></span>');
        } else if ($(element).hasClass('valid')) {
            alert("valid");
            $(element).closest('li').find('label>span:first').html('<span class="validate_icon valid"> <span class="icon-stack"><i class="icon-sign-blank icon-stack-base"></i><i class="icon-ok"></i></span></span>');
        }
    }
});

PHP Code that handles the email exists:

public function email_exists() {
    $this->autoRender = false;
    if($this->request->is('post')) {
        $this->RequestHandler->respondAs('json');
        if(!$this->User->findByEmail($this->request->data['User']['email'])) {
            echo json_encode(true);
        } else {
            echo json_encode(false);
        }
    }
}

I have also tried simply echo "true"; and echo 1; I have tried everything suggested in the comments below but regardless - the problem exists.

Was it helpful?

Solution

I had the exact same problem, and by seeing your code I might say that you have the same cause, but let's break it down.

Checking

First, let's check that my comment is relevant, and I can actually help you. Comment the remote param on your email validation set up:

"data[User][email]": {
            required: true,
            email: true
        },

Is your problem fixed? Great, keep reading (feel free to skip to the fix section).

The problem

1. When the plugin validates, it creates a list of errors, stored into an array called "errorList".

2. Have you ever used the showErrors functionality? It's there to show all the errors, but also to target-show errors. If you want to show specific errors, or to show errors that are out of the limits of the plugin (ej.: a 60s timeout has expired), you can use that method.

3. When showing specific errors, what that method does is to add the specified error(s) to the errorList.

4. The problem is that before adding new errors that list is cleared up (I didn't write the code, but it seems that it's done in order to keep that list nice and clean, and not having two different errors of the same input).

5. Now, when the email is checked remotely we are in the same situation of a timeout. So it uses the showErrors functionality, and that means that the form is validated when click, and some seconds later (with the PHP response), the email error is shown, but clearing up the errorList. That's what is happening.

The fix

  1. If you are not going to do explicit use of showErrors, truth is that you can comment the line where the errorList is cleared up:

    showErrors: function( errors ) {
        if ( errors ) {
            // add items to error list and map
            $.extend( this.errorMap, errors );
            //this.errorList = [];
            for ( var name in errors ) {
            ...
    
  2. If you are going to do an explicit use of that method, you can try this version instead. Doesn't clear the error list, but checks that you're not adding the same error twice:

    showErrors: function( errors ) {
        if ( errors ) {
            // add items to error list and map
            $.extend( this.errorMap, errors );
            for ( var name in errors ) {
                var tempElem = this.findByName(name)[0];
                this.errorList = jQuery.grep(this.errorList, function( error, i ) {
                    return error.element != tempElem;
                });
                this.errorList.push({
                    message: errors[name],
                    element: tempElem
                });
            }
    

Let me know if worked or you have any problem.

OTHER TIPS

This code of yours can be a problem...

onfocusout: function (element) {
    $(element).valid();
},

You cannot put the .valid() method inside of the .validate() method without causing some serious issues.

This is the default onfocusout function from the plugin...

onfocusout: function( element, event ) {
    if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
        this.element(element);
    }
}

What's the purpose of your custom onfocusout function? Generally, it's not needed since the onfocusout trigger is already built into the functionality. One constructs their own onfocusout function only to over-ride the built-in default. So if you want the default onfocusout behavior, just remove the onfocusout option entirely.

If you really want to emulate something like in your code, it would need to look like this...

onfocusout: function(element, event) {
    this.element(element);
}

Quote OP Comment:

"as I said im not really sure what good it would do you: (I cant get it to format here..)"

$this->RequestHandler->respondAs('json');
if(!$this->User->findByEmail($this->request->data['User']['email'])) { 
    return json_encode(true);
} else { 
    return json_encode(false);
}

It does a lot of good to show any code that could be affecting the problem, especially any code that's wrong. This could have been solved two days ago.

return is for returning to the PHP function that called this one. In other words, return will do nothing here since there is no PHP function to return to. On the other hand, echo will output from PHP... and that's what you need for jQuery Validate remote...

if (....) { 
    echo true;
} else { 
    echo false;
}

PHP return versus PHP echo

Also see: https://stackoverflow.com/a/21313309/594235

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top