33

Usually, HTML5 form validation runs before the submit event.

With this

<form id="myForm">
    <input  type="text" name="foo" required />

    <button type="submit"> Submit </button>

</form>

<script>
    $("#myForm").on('submit',function(){
        console.log("I'm entering the submit event handler");
    });
</script>

if the input field is empty, the submit event handler doesn't run. It will be triggered only if the HTML5 validation (the required attribute, in this case) has passed.

I'd expect a captcha to run after the HTML5 validation too; why do I have to annoy the user compiling a captcha if later on I'd warn him there are missing fields ? First I should force the user to do everything in the right way, then ensure it's a human and not a bot, IMHO.

Appearently, reCaptcha does something on the form it attaches on, removing the HTML5 validation feature.

For example, using the latest Invisible reCaptcha:

<form id="myForm">
    <input  type="text" name="foo" required />

    <button type="submit"
           class="g-recaptcha" 
    data-sitekey="your_site_key" 
   data-callback='myCallback'> Submit </button>

</form>

<script>
    function myCallback(token){
        $("#myForm").submit();
    }

    $("#myForm").on('submit',function(){
        console.log("I'm entering the submit event handler");
    });
</script>

The form will be submitted with the empty field, without notifying the user about its obligatoriness.

Any clue on why it acts like this ? Is there a way I can instruct reCaptcha to let HTML5 form validation run before taking control ?

Andrea Ligios
  • 49,480
  • 26
  • 114
  • 243

2 Answers2

53

Instead of attaching the reCAPTCHA attributes to the button directly you have to add them to a div and then use grecaptcha.execute(); on form submit.

<script src="https://www.google.com/recaptcha/api.js" async defer></script>

<form id="myForm">
    Name: (required) <input id="field" name="field" required>
    <div id='recaptcha' class="g-recaptcha"
         data-sitekey="your_site_key"
         data-callback="onCompleted"
         data-size="invisible"></div>
    <button id='submit'>submit</button>
</form>
<script>
    $('#myForm').submit(function(event) {
        console.log('validation completed.');

        event.preventDefault(); //prevent form submit before captcha is completed
        grecaptcha.execute();
    });

    onCompleted = function() {
        console.log('captcha completed.');
    }
</script>

When you add the reCAPTCHA attributes to the button as you did, Google simply adds a click listener to the button and executes the reCAPTCHA when the button is clicked. There is no submit listener added. The HTML5 validation is only triggered by a click on a submit button and runs before the submit event. That is why we must make sure that reCAPTCHA runs after the submit event. Apparently the click event, which reCAPTCHA subscribes to, is dealt with before the internal HTML5 validation would get triggered. The validation is also not triggered when you submit a form using submit(). That is just how that function got defined. Because you call the function in your callback, validation gets not triggered. However even if you did not call the submit() function in the callback it seems that reCAPTCHA stops the event from its default behaviour and thus stops validation completely.

Submit form after reCAPTCHA completion

If you want to get the form submitted after the reCAPTCHA is completed, you can check for the result of grecaptcha.getResponse().

<script src="https://www.google.com/recaptcha/api.js" async defer></script>

<form id="myForm">
    Name: (required) <input id="field" name="field" required>
    <div id='recaptcha' class="g-recaptcha"
         data-sitekey="your_site_key"
         data-callback="onCompleted"
         data-size="invisible"></div>
    <input type="submit" value="submit" />
</form>
<script>
    $('#myForm').submit(function(event) {
        console.log('form submitted.');

        if (!grecaptcha.getResponse()) {
            console.log('captcha not yet completed.');

            event.preventDefault(); //prevent form submit
            grecaptcha.execute();
        } else {
            console.log('form really submitted.');
        }
    });

    onCompleted = function() {
        console.log('captcha completed.');
        $('#myForm').submit();
        alert('wait to check for "captcha completed" in the console.');
    }
</script>
Andrea Ligios
  • 49,480
  • 26
  • 114
  • 243
maechler
  • 1,257
  • 13
  • 18
  • Extremely neat answer! Still don't know why attaching the event on the `onClick` handler should impact on the `onSubmit` handler killing its inbuilt validation capabilities, but that's a minor question: my needs were quite complex (this was just the first piece of the puzzle) and they're full working now. Thanks! – Andrea Ligios May 17 '17 at 16:40
  • I edited the answer to respond more to that part of the question you asked in the post. You're welcome! – maechler May 18 '17 at 07:37
  • Thank you for your clarifications, much appreciated! Beware of not reaching 20 edits (you're at 11), or the post will be converted in a Community Wiki and you will lose the reputation gained :) – Andrea Ligios May 18 '17 at 07:41
  • Thanks for the hint, also much appreciated. No more edits now ;) – maechler May 18 '17 at 07:43
  • 3
    This is an amazing answer, yet it was so hard to find it. – amosmos Feb 03 '20 at 16:34
  • 1
    Perfect solution, you saved my night! – Rustam Shafigullin Aug 07 '20 at 17:21
  • The only correct answer online. The Google documentation on this sucks. It's amazing because you would think more people are using both HTML5 validation and reCAPTCHA! – stepanian Aug 13 '20 at 07:07
  • Finally fixed my problem thank you so much! I used this in my Symfony project, if anyone has more questions on my implementation please follow up with me! – Lushawn Oct 12 '22 at 14:21
  • Not good. If Google decides you're a bot, it will trigger the image verification, and your form will submit before you complete that verification. – FiddlingAway Jan 18 '23 at 15:27
6

maechler's answer is excellent, however, if one does not want to use jQuery, you can add the event listener with an iife

(function() {
    document.getElementById("my-form").addEventListener("submit", function(event) {
        console.log('form submitted.');
        if (!grecaptcha.getResponse()) {
            console.log('captcha not yet completed.');

            event.preventDefault(); //prevent form submit
            grecaptcha.execute();
        } else {
            console.log('form really submitted.');
        }
      });
  })();

I hope that helps.

lauchness
  • 339
  • 3
  • 6