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.
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>
grecaptcha.execute()
submits it WHEN the reCAPTCHA form test is correctly done. –
Medievalist formSubmit()
has to be empty?! –
Oneiric grecaptcha.reset();
? Is that for Ajax forms only? –
Haplo 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>
addEventListener
e.g. document.addEventListener('DOMContentLoaded', function () { document.getElementById('html-submitt') .addEventListener('submit', formSubmitBtn); });
–
Competitive 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? –
Exostosis $event
. Is it still coming through as expected? Is token created from the execute
method? Is it attaching the token value to the correct form? If you make a codepen or something similar I could help debug. –
Exostosis grecaptcha.execute("RECAPTCHA_SITE_KEY", { action: 'submit' }).then(function(token) {
does not log at the time a valid form is attempted to submit, after the execute promise, then something else is wrong related to the setup of the API key, or the way the formSubmitBtn method is being called. Are you on a domain that is whitelisted in your Google apps API? Are you getting any other errors thrown in the console? –
Exostosis 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
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.
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
Since September 2022, all major browsers have supported the requestSubmit()
method (Safari was the latecomer, in case anybody is interested) which does trigger the browser's built-in constraint validation and raises the submit
event.
So we can now just modify Recaptcha's recommended code by changing its call to the submit()
method to a call to the requestSubmit()
method:
<script>
function onSubmit(token) {
document.getElementById("demo-form").submit();
}
</script>
becomes
<script>
function onSubmit(token) {
document.getElementById("demo-form").requestSubmit();
}
</script>
© 2022 - 2025 — McMap. All rights reserved.