How do I defer Google Recaptcha V3

9 posts by 2 authors in: Forums > CMS Builder
Last Post: August 5, 2022   (RSS)

By JeffC - July 30, 2022

Hi

I am using the Google Recaptcha V3 plugin (recaptchaPluginV3.php). Everything works great without using async but it's slowing the loading of my page. To defer it I assume I need to add async to line 167 of the plugin like this: 

function recaptcha_displayHeaders($action = 'default') {

  if(isset($GLOBALS['INCLUDE_RECAPTCHA'])):

  ?>

    <script async src="https://www.google.com/recaptcha/api.js?render=<?php echo urlencode(recaptcha_settings('site_key')); ?>"></script>

    <script>

    grecaptcha.ready(function() {

        grecaptcha.execute('<?php echo urlencode(recaptcha_settings('site_key')); ?>', {action: <?php echo json_encode(recaptcha_settings('page')); ?>}).then(function(token) {

          document.getElementById("g-recaptcha-response").value = token;

        });

    });

    </script>

  <?php

But this results in the error: Recaptcha response not found, please ensure JavaScript is enabled in your browser.

I've found some documentation that should help, but I can't fathom it out. https://developers.google.com/recaptcha/docs/loading

The docs say:

When loading reCAPTCHA asynchronously, keep in mind that reCAPTCHA cannot be used until it has finished loading… In some situations, adjusting script ordering can be enough to prevent race conditions. Alternatively, you can prevent race conditions by including the following code snippet on pages that load reCAPTCHA. If you are usinggrecaptcha.ready() to wrap API calls, add the following code snippet to ensure that reCAPTCHA can be called at any time.

I would appreciate a bit of help incorporating this, as when I try I still get an error.

<script async src="https://www.google.com/recaptcha/api.js"></script>
<script>
  // How this code snippet works:
  // This logic overwrites the default behavior of `grecaptcha.ready()` to
  // ensure that it can be safely called at any time. When `grecaptcha.ready()`
  // is called before reCAPTCHA is loaded, the callback function that is passed
  // by `grecaptcha.ready()` is enqueued for execution after reCAPTCHA is
  // loaded.
  if(typeof grecaptcha === 'undefined') {
    grecaptcha = {};
  }
  grecaptcha.ready = function(cb){
    if(typeof grecaptcha === 'undefined') {
      // window.__grecaptcha_cfg is a global variable that stores reCAPTCHA's
      // configuration. By default, any functions listed in its 'fns' property
      // are automatically executed when reCAPTCHA loads.
      const c = '___grecaptcha_cfg';
      window[c] = window[c] || {};
      (window[c]['fns'] = window[c]['fns']||[]).push(cb);
    } else {
      cb();
    }
  }

  // Usage
  grecaptcha.ready(function(){
    grecaptcha.render("container", {
      sitekey: "ABC-123"
    });
  });
</script>
Jeff

By daniel - August 2, 2022

Hi Jeff,

I believe in Google's example, you're supposed to replace the "grecaptcha.ready" portion with what you actually want to use. So I think it would look something like this: 

<script async src="https://www.google.com/recaptcha/api.js"></script>
<script>
  // How this code snippet works:
  // This logic overwrites the default behavior of `grecaptcha.ready()` to
  // ensure that it can be safely called at any time. When `grecaptcha.ready()`
  // is called before reCAPTCHA is loaded, the callback function that is passed
  // by `grecaptcha.ready()` is enqueued for execution after reCAPTCHA is
  // loaded.
  if(typeof grecaptcha === 'undefined') {
    grecaptcha = {};
  }
  grecaptcha.ready = function(cb){
    if(typeof grecaptcha === 'undefined') {
      // window.__grecaptcha_cfg is a global variable that stores reCAPTCHA's
      // configuration. By default, any functions listed in its 'fns' property
      // are automatically executed when reCAPTCHA loads.
      const c = '___grecaptcha_cfg';
      window[c] = window[c] || {};
      (window[c]['fns'] = window[c]['fns']||[]).push(cb);
    } else {
      cb();
    }
  }

    grecaptcha.ready(function() {
        grecaptcha.execute('<?php echo urlencode(recaptcha_settings('site_key')); ?>', {action: <?php echo json_encode(recaptcha_settings('page')); ?>}).then(function(token) {
          document.getElementById("g-recaptcha-response").value = token;
        });
    });
</script>

Can you try that out, and let me know if it fixes the issue?

Thanks!

Daniel
Technical Lead
interactivetools.com

By daniel - August 3, 2022

Hey Jeff,

I didn't notice that the second snippet was missing the API key when loading api.js - that may be the issue here. Can you try this out?

<script async src="https://www.google.com/recaptcha/api.js?render=<?php echo urlencode(recaptcha_settings('site_key')); ?>"></script>
<script>
  // How this code snippet works:
  // This logic overwrites the default behavior of `grecaptcha.ready()` to
  // ensure that it can be safely called at any time. When `grecaptcha.ready()`
  // is called before reCAPTCHA is loaded, the callback function that is passed
  // by `grecaptcha.ready()` is enqueued for execution after reCAPTCHA is
  // loaded.
  if(typeof grecaptcha === 'undefined') {
    grecaptcha = {};
  }
  grecaptcha.ready = function(cb){
    if(typeof grecaptcha === 'undefined') {
      // window.__grecaptcha_cfg is a global variable that stores reCAPTCHA's
      // configuration. By default, any functions listed in its 'fns' property
      // are automatically executed when reCAPTCHA loads.
      const c = '___grecaptcha_cfg';
      window[c] = window[c] || {};
      (window[c]['fns'] = window[c]['fns']||[]).push(cb);
    } else {
      cb();
    }
  }

    grecaptcha.ready(function() {
        grecaptcha.execute('<?php echo urlencode(recaptcha_settings('site_key')); ?>', {action: <?php echo json_encode(recaptcha_settings('page')); ?>}).then(function(token) {
          document.getElementById("g-recaptcha-response").value = token;
        });
    });
</script>

If that doesn't work, feel free to send a 2nd-level support request (https://www.interactivetools.com/support/request/) and I can take a look at it directly.

Thanks,

Daniel
Technical Lead
interactivetools.com

By JeffC - August 3, 2022

Hi Daniel

Thanks for the reply. I did notice the missing API key in your first post and tried with and without.

I won't post a 2nd level request. I don't have budget to fix it, and the problem is only a small one. I aim to make my pages load as fast as they can, but this one is actually pretty quick already.

Thanks for trying.

Jeff

By daniel - August 4, 2022

Hey Jeff,

This is something I could check out at no charge - it'd be useful for us to know as well. But as it's a minor issue I also understand if it's not worth the trouble.

Take care,

Daniel
Technical Lead
interactivetools.com

By JeffC - August 4, 2022 - edited: August 4, 2022

Thanks Daniel, I'll submit the request

Jeff

By daniel - August 4, 2022

Thanks John!

It appears that the script provided in Google's docs is slightly inaccurate - at least for our usage here - so I've made a small change and it appears to be working with async. For anyone else reading, here is the final updated script:

    <script async src="https://www.google.com/recaptcha/api.js?render=<?php echo urlencode(recaptcha_settings('site_key')); ?>"></script>

    <script>
        
      if(typeof grecaptcha === 'undefined') {
        grecaptcha = {};
      }
      grecaptcha.ready = function(cb){
        if(typeof grecaptcha.execute === 'undefined') {
          // window.__grecaptcha_cfg is a global variable that stores reCAPTCHA's
          // configuration. By default, any functions listed in its 'fns' property
          // are automatically executed when reCAPTCHA loads.
          const c = '___grecaptcha_cfg';
          window[c] = window[c] || {};
          (window[c]['fns'] = window[c]['fns']||[]).push(cb);
        } else {
          cb();
        }
      }
    
      grecaptcha.ready(function(){

          grecaptcha.execute('<?php echo urlencode(recaptcha_settings('site_key')); ?>', {action: <?php echo json_encode(recaptcha_settings('page')); ?>}).then(function(token) {

            document.getElementById("g-recaptcha-response").value = token;

          });

      });

    </script>

Cheers,

Daniel
Technical Lead
interactivetools.com

By JeffC - August 5, 2022

Spot on, thanks!

Jeff