Using Google reCaptcha with forms that already have submit event handlers.

We had to jump through some hoops to get Google reCAPTCHA v3 to work with OpenCart 4.0.2.3 data-oc-toggle="ajax" form attributes. reCAPTCHA implementation is simple and straight-forward, and I really liked the ajax form processor instead of the post/response/redirect flow. I was not aware however that those new ajax forms were going to be a headache while writing a our grecaptcha module.

Other developers opt to populate the challenge response during page load, even though that can easily result in expired challenges, and is discouraged by Google. So we attempted various ways to call grecaptcha.execute() before form submission, but simply could not trigger our listener before other listeners that were added earlier in the DOM.

We finally ended up adding a click event listener to document.body. When triggered, we check to see if it came from a submit button, and then look for our recaptcha field. In which case, we kill the click event and trigger grecaptcha.execute(), meaning it fires when necessary and not on page load.

The final touch to get it working as desired was for our new submit event to bubble and be cancelable, so our handler does not interrupt the flow of other submit event listeners.

<input type="hidden" id="FIELD" name="FIELD">
<script src="https://www.google.com/recaptcha/api.js?render=KEY"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
    document.body.addEventListener('click', function (event) {
        if (event.target.type == 'submit' && event.target.form.elements.FIELD) {
            event.preventDefault();

            grecaptcha.ready(function() {
                grecaptcha.execute('KEY', { action: 'ACTION' }).then(function (token) {
                    let element = document.getElementById('FIELD');
                    
                    element.value = token;
                    
                    const submit = new Event('submit', {
                        bubbles: true,
                        cancelable: true
                    });
                    
                    element.form.dispatchEvent(submit);
                });
            });
        }
    });
});
</script>

We intend to add explicit rendering so we can incorporate grecaptcha.render() parameters. Check back later for that update 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *