Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google INVISIBLE reCaptcha + jQuery validate() issue

I have implemented new Google's INVISIBLE reCaptcha on several sites successfully, however, it conflicts with jQuery validate(), in such a way that the js validation is no longer executed on button/form submit, and reCaptcha instantly kicks-in. If user forgets to fill-in a requried form field, it will have to wait for server response, and get back to it next time.

jQuery .validate() supports callback, but because of the fact that it is hard-coded inside an internal class of a CMS, is there a way I can modify it somehow outside (e.g. from front-end theme, on document load, when validation code is already rendered)?

Or, another idea would be to postpone captcha's callback somehow, so that validation step can get a chance to run.

Thanks!

jQuery Validate form: (hard-coded in the core, can't be modified, unless I edit core file or extend class/clone function - not optimal)

<script type="text/javascript">
$(document).ready(function(){
    $("form[name=comment_form]").validate({
        rules: {
            body: {
                required: true,
                minlength: 1
            },
            authorEmail: {
                required: true,
                email: true
            }
        },
        wrapper: "li",
        errorLabelContainer: "#comment_error_list",
        invalidHandler: function(form, validator) {
            $('html,body').animate({ scrollTop: $('#comment_error_list').offset().top }, { duration: 250, easing: 'swing'});
        },
        submitHandler: function(form){
            $('button[type=submit], input[type=submit]').attr('disabled', 'disabled');
            form.submit();
        }
    });
});
</script>

reCaptcha explicit render:

<script type='text/javascript'>
    var renderInvisibleReCaptcha = function() {
    for (var i = 0; i < document.forms.length; ++i) {
        var form = document.forms[i];
        var holder = form.querySelector('.invisible-recaptcha');
        if (null === holder) continue;
        (function(frm){
            var holderId = grecaptcha.render(holder, {
            'sitekey': 'my-key-here',
            'badge': 'inline',
            'size': 'invisible',
            'callback': function (recaptchaToken) {HTMLFormElement.prototype.submit.call(frm);},
            'expired-callback': function(){grecaptcha.reset(holderId);}
            });
            frm.onsubmit = function (evt){evt.preventDefault();grecaptcha.execute(holderId);};
        })(form);
    }
};
</script>
like image 595
dev101 Avatar asked Mar 11 '17 20:03

dev101


3 Answers

I had the same problem, I finally found the right way to integrate jquery validate with invisible reCaptcha. It's a mix of some of the proposed solutions here.

First of all, the the HTML part:

<div class="g-recaptcha" data-sitekey="<your key here>"
    data-size="invisible" 
    data-callback="formSubmit">
</div>

Then, you must implement the reCaptcha callback in that way:

function formSubmit(response) {
    // submit the form which now includes a g-recaptcha-response input
     $("#orderform").submit();
    return true;
}

And finally, the tricky part is in the jquery validate's submitHandler:

   submitHandler: function (form) {
        if (grecaptcha.getResponse()) {
                // 2) finally sending form data
                form.submit();
        }else{
                // 1) Before sending we must validate captcha
            grecaptcha.reset();
            grecaptcha.execute();
        }           
    }

The sequence is as follows:

  1. When user hits submit button, jquery's submitHandler is called, as invisible recaptcha isn't executed yet, we call grecaptcha.execute(). It takes some seconds for google's recaptcha to validate, and when it's fully validated it'll call formSubmit callback (while this callback isn't called, we cannot send form data to the server!).
  2. In formSubmit callback we call $('#orderform').submit to force to enter submitHandler again.
  3. Inside submitHandler again, this time as grecaptcha.getResponse is not null, we can post the form data to the server, it will include the recaptcha hidden field, which must then be validated on the server side.

Hope this helps.

like image 117
JoanF Avatar answered Oct 21 '22 01:10

JoanF


I was having the similar problem and validated the form before executing invisible reCaptcha with the help of this link provided by Terry.

Here are the steps:

Add this div before the submit button in your form

 <div id='recaptcha' class="g-recaptcha"
      data-sitekey="your_site_key"
      data-callback="onSubmit"
      data-size="invisible"></div>

Update your key as well. After the form closure add this method

<script>onload();</script>

Before your form add this code

<script>
function validate(event) {
 event.preventDefault();
 if (!document.getElementById('field').value) {
   alert("You must add text to the required field");
 } else {
   grecaptcha.execute();
 }
}

function onload() {
 var element = document.getElementById('submit');
 element.onclick = validate;
}
</script>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>

Do all the validations in validate method and if all the validations are successful then execute captcha.

like image 25
Abdul Jamil Avatar answered Oct 21 '22 02:10

Abdul Jamil


Technically all we "really" want to do is Programmatically bind the challenge to a button. For example:

Add this method, which we want only to execute the invisible grecaptcha instead of "real" validation

 jQuery.validator.addMethod("iv_recapcha_valid", function(value, element) {
        return grecaptcha.execute();
    }, 'No message needed, grecaptcha excecuted'); 

Then add this rule to your existing rules

rules: { gRecaptchaResponse: { iv_recapcha_valid: true } }

add a hidden field

<input type="hidden" name="gRecaptchaResponse" value="" >

Now, "technically" the submit button is bound to the user's click action

https://developers.google.com/recaptcha/docs/invisible

like image 31
Christian Žagarskas Avatar answered Oct 21 '22 03:10

Christian Žagarskas