I integrated the new hidden reCAPTCHA (v2) framework which by default verifies the user with the click
event of the submit button. But this event is triggered before the built-in HTML form validation. I am looking for a way to make it in the expected order: form validation first, reCAPTCHA after.
8 Answers
You have to do it programmatically thanks to a new v2 grecaptcha
method: grecaptcha.execute()
so that recaptcha doesn't replace the button's default click event which was preventing the default HTML5 form validation.
The event path is:
- Submit button click event: browser built-in form validation
- Form submit event: call
grecaptcha.execute()
- reCAPTCHA callback: submit the form
$('#form-contact').submit(function (event) {
event.preventDefault();
grecaptcha.reset();
grecaptcha.execute();
});
function formSubmit(response) {
// submit the form which now includes a g-recaptcha-response input
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.google.com/recaptcha/api.js"></script>
<form action="?">
<div class="g-recaptcha"
data-sitekey="your-key"
data-size="invisible"
data-callback="formSubmit">
</div>
<button type="submit">Submit</button>
</form>
-
4Using this code, the form will never submit, because it is preventing the default action (which is submit). If you wanna submit, you gotta not prevent the action due to a condition, like my answer below, please check there. Mar 10, 2017 at 21:04
-
1@giovannipds this code is running on our website and it does submit oO
grecaptcha.execute()
submits it WHEN the reCAPTCHA form test is correctly done. Mar 14, 2017 at 21:56 -
2@JulioGuerra you're right, but it doesn't use browser validation default unfortunately. I guess mine below does. Try to check it out. Jun 9, 2017 at 18:25
-
3
-
2@Black not necessarily. The documentation says "The user's response, g-recaptcha-response, will be the input for your callback function." I changed the example to include it instead of ignoring it (because you can also read it from an hidden input in the form, once validated). Nov 21, 2017 at 13:57
Here is my solution to get HTML5 validation + Invisible recaptcha:
HTML:
<form id="my-form">
<!-- Your form fields ... -->
<div class="g-recaptcha"
data-sitekey="..."
data-callback="submitMyForm"
data-size="invisible">
</div>
<button type="submit">Submit</button>
</form>
JS:
var myForm = $('my-form');
function submitMyForm () {
myForm.trigger('submit', [true]);
}
$(function () {
myForm.on('submit', function (e, skipRecaptcha) {
if(skipRecaptcha) {
return;
}
e.preventDefault();
grecaptcha.execute();
});
})
Hi got a working solution here. Working with invisible Recaptcha.
jQuery(document).ready(function() {
var commentform = jQuery("#commentform");
commentform.on("click", "#submit-comment", function(e) {
if(commentform[0].checkValidity()) {
e.preventDefault();
grecaptcha.execute();
}
});
});
function submitCommentForm(data) {
document.getElementById("commentform").submit();
}
<form action="blaba.php" method="post" id="commentform" class="comment-form">
<div class="form-submit">
<div data-callback="submitCommentForm" data-sitekey="yourkey" class="g-recaptcha" data-size="invisible">
<button id="submit-comment">Leave a comment</button>
</div>
</form>
Here's my solution.
- Uses reCaptcha v3 (invisible) docs
- Uses native HTML5 form validation
- Uses pure JS
- Uses standard POST processing (can be modified to AJAX)
Add as many forms as needed, just change the 'UNIQUE_FORM_ID' in the two places, and update the POST_URL for the form. Ensure you use your own key in the locations of 'RECAPTCHA_SITE_KEY'.
<form id="UNIQUE_FORM_ID" method="post" action="POST_URL">
<!-- ** Notice ** this hidden input field that will later send our g-recaptcha token back to our server -->
<input type="hidden" name="g-recaptcha-response" value="">
<!-- Add other hidden nonce fields -->
<!-- Required field -->
<input name="fullname" type="text" placeholder="Full Name" required>
<!-- Submit button -->
<!-- ** Notice ** the 'form' attribute; using SAME value as it's parent's form id, above. -->
<!-- ** Notice ** the 'onclick' attribute; be sure to pass event -->
<button type="submit" form="UNIQUE_FORM_ID" onclick="formSubmitBtn(event)">Send</button>
</form>
<!-- Only add scripts once -->
<!-- ** Notice ** to manually call grecaptcha, our site key must be included when loading api.js using the 'render' query param -->
<script src="https://www.google.com/recaptcha/api.js?render=RECAPTCHA_SITE_KEY"></script>
<script>
/**
* Handles form submissions for Google recaptcha v3.
* Allows for HTML5 form validation to complete before processing.
*/
function formSubmitBtn($event) {
/**
* Checks the validity of the form.
* Return if invalid; HTML5 validation errors should display.
*/
if (!$event.target.form.checkValidity()) {
return;
}
/**
* Form is client-side valid; taking over the remainder of processing.
*/
$event.preventDefault();
grecaptcha.ready(function() {
grecaptcha.execute("RECAPTCHA_SITE_KEY", { action: 'submit' }).then(function(token) {
/**
* Adds the token g-recaptcha-response token to our hidden form element.
* ** Notice ** we our referencing the specific form's input element by name here (do not use IDs).
*/
$event.target.form.elements['g-recaptcha-response'].value = token;
/**
* Use the form API directly to submit the form.
*/
$event.target.form.submit();
});
});
}
</script>
-
I stumbled across your post for a similar solution. Is this intended to be purely client-side? The token doesn't appear to be populated. Secondly, is there any reason why you chose not to use
addEventListener
e.g.document.addEventListener('DOMContentLoaded', function () { document.getElementById('html-submitt') .addEventListener('submit', formSubmitBtn); });
Jan 10, 2021 at 3:47 -
This is intended to be flexible. There is no server dependency, if that's what you're asking. Of course you'll need to use your private api keys to send the response token to google to get the recaptcha values. Did you replace
RECAPTCHA_SITE_KEY
with your specific key? I'm running this code in a production environment and it is working for me. I do not see a reason to add event listeners and consume resources and risk memory leaks from not removing event listeners, etc (depending on the usage). The one function will support unlimited forms. Happy to help further. Code snippet?– PigBoTJan 10, 2021 at 20:59 -
The code is being run verbatim with an updated recaptcha key. When the form is submitted, it performs the native HTML5 validation (which is a plus). It then captures the values e.g. name. It does not however return a token in the hidden input. It's blank. The reason for using event listeners is because inline click events are blocked by content security policies. Jan 10, 2021 at 21:59
-
I'm not a CSP expert. Lots of questions--but not the place in these comments. :P It does sound like a reasonable workaround for your situation. There are so many places that could go wrong with the change, notably around the
$event
. Is it still coming through as expected? Is token created from theexecute
method? Is it attaching the token value to the correct form? If you make a codepen or something similar I could help debug.– PigBoTJan 11, 2021 at 15:28 -
I'm unsure if Codepen or anything similar has the option to implement a CSP policy as this tends to be server side. Happy to share the content security policy if you like. Do you mean is it coming through on load? If yes, no there is no token on load. No token is generated either when the form is submitted. The hidden value is empty. Jan 11, 2021 at 21:34
I had this problem as the default method seems to override the html5 form validation. I also wanted all code to be generic rather than hard coding any functions/element names. In the end I came up with the following code using the v3 api -
HTML
<form method="post" action="?" class="ui-recaptcha" name="my_form_name">
...
<input type="submit" value="Submit">
</form>
<script src="//www.google.com/recaptcha/api.js?render={key}" async defer></script>
Javascript (I'm using jQuery but would be fairly easy to adapt to vanilla js)
$('.ui-recaptcha').submit(e => {
var form = e.target;
if( $(form).data('recaptcha-done') )
return;
e.preventDefault();
grecaptcha.execute('{key}', {'action': $(form).attr('name')}).then(token => {
$(form).append($('<input>').attr({'type': 'hidden', 'name': 'g-recaptcha-response', 'value': token}));
$(form).data('recaptcha-done', true);
$(form).submit();
});
});
I found that just calling submit
as in some examples above caused a loop for me, which would make sense seeing as the recaptcha handler runs on the submit
event.
This runs recaptcha for any ui-recaptcha
form, passes the form name
attribute as the action which can be seen in reCaptcha console, and then inserts the token into the form. Once run it sets a data attribute on the form so the recursive call to submit doesn't try to run recaptcha again.
This solution is similar to solution by @PigBoT but with the addition of reportValidity() and is using ReCAPTCHA v3
Credit to https://github.com/ambethia/recaptcha/issues/302#issuecomment-621794131
<script src="https://www.google.com/recaptcha/api.js"></script>
<script type="text/javascript">
function contactOnSubmit(token) {
var contactForm = document.getElementById('contactUs');
if(contactForm.checkValidity()) {
//SERVER SIDE VALIDATION here,
//on success, contactForm.submit();
} else {
grecaptcha.reset();
contactForm.reportValidity();
}
}
</script>
Form (id="contactUs")
<button class="g-recaptcha" data-sitekey="..." data-callback="contactOnSubmit" data-action="submit">Submit</button>
"Can I Use" site currently reports 97% uses have support for checkValidity() https://caniuse.com/?search=checkValidity
let siteKey = "...";
$("form").submit(function (eventObj) {
var myForm = this;
eventObj.preventDefault();
grecaptcha.execute( siteKey, {
action: "submit"
})
.then(function (token) {
$('<input />').attr('type', 'hidden')
.attr('name', "g_recaptcha_response")
.attr('value', token)
.appendTo(myForm);
myForm.submit();
});
});
This will execute recapcha, wait for response, add hidden attribute g_recaptcha_response to any form when browser try to submit it and then actually submit it. You need global variable siteKey
I was wanting the same behavior, but using the new recaptcha, the invisible one. After looking at some code and testing some stuff, I got into this. The main difference is that this uses the default browser validation as well:
var contact_form;
$(function() {
contact_form = $('#contact-form');
contact_form.submit(function (event) {
if ( ! contact_form.data('passed')) {
event.preventDefault();
grecaptcha.execute();
}
});
});
function sendContactForm(token) {
contact_form.data('passed', true);
contact_form.submit();
}
It basically stores the jquery form object in a global var, including, it uses sendContactForm
as the callback, but when called by the recaptcha, it sets a data var named passed
, which allows the form to not be prevented. It's exactly the same behavior as recaptcha would normally do, but with that condition.
Update: re-looking at my code right reminds me that it probably needs a way to restore data passed to false
after grecaptcha's execution. Consider that if you'll implement this.
-
-
No, if I remember well, your answer above doesn't use browser validation defaults, mine does. Consider testing both before leaving -1. Thanks. Jun 9, 2017 at 18:22