119

I have 2 forms on a single page. One of the forms has a Recaptcha displaying all the time. The other should display a Recaptcha only after a certain event such as maxing out login attempts. So there are times when I would need 2 Recaptchas to appear on the same page. Is this possible? I know I could probably use a single one for both, but the way I have the layout, I would much prefer to have 2. Thanks.

Update: well I guess it may not be possible. Can anybody recommend another capture library to use side by side with reCaptcha? I really want to be able to have 2 captchas on the same page.

Update 2: What if I put each form in an iframe? Would this be an acceptable solution?

4
  • Couldn't you just display the same one twice? Aug 6, 2009 at 22:50
  • 1
    I tried that..when I try to duplicate the captcha code it will just display the captcha that comes first
    – oym
    Aug 6, 2009 at 22:51
  • 1
    Anyone coming across this for the new recaptcha API, this is possible by using the explicit loading described with examples in the docs at developers.google.com/recaptcha/docs/display#recaptcha_methods
    – El Yobo
    Apr 16, 2015 at 4:25
  • 3
    An iframe should be possible but is a bad way of solving the problem, compared to using JavaScript like in Hüseyin Yağlı's answer. Most browsers should support JavaScript and the default reCAPTCHA uses JavaScript anyway. I don't know though what would need to be done to solve the problem without JavaScript support though.
    – Edward
    Apr 11, 2016 at 14:50

17 Answers 17

225

With the current version of Recaptcha (reCAPTCHA API version 2.0), you can have multiple Recaptchas on one page.

There is no need to clone the Recaptcha nor try to workaround the problem. You just have to put multiple <div> elements for the Recaptchas and render the Recaptchas inside them explicitly.

This is easy with the Google Recaptcha API. Here is the example HTML code:

<form>
    <h1>Form 1</h1>
    <div><input type="text" name="field1" placeholder="field1"></div>
    <div><input type="text" name="field2" placeholder="field2"></div>
    <div id="RecaptchaField1"></div>
    <div><input type="submit"></div>
</form>

<form>
    <h1>Form 2</h1>
    <div><input type="text" name="field3" placeholder="field3"></div>
    <div><input type="text" name="field4" placeholder="field4"></div>
    <div id="RecaptchaField2"></div>
    <div><input type="submit"></div>
</form>

In your Javascript code, you have to define a callback function for Recaptcha:

<script type="text/javascript">
    var CaptchaCallback = function() {
        grecaptcha.render('RecaptchaField1', {'sitekey' : '6Lc_your_site_key'});
        grecaptcha.render('RecaptchaField2', {'sitekey' : '6Lc_your_site_key'});
    };
</script>

After this, your Recaptcha script URL should look like this:

<script src="https://www.google.com/recaptcha/api.js?onload=CaptchaCallback&render=explicit" async defer></script>

Or instead of giving IDs to your Recaptcha fields, you can give a class name and loop these elements with your class selector and call .render().

10
  • 1
    Coincidentally, do you know how to add hidden recaptcha field for jquery validation for each field?, currently I have it working only with 1 recaptchafield but can it work with two recaptchas? <input type="hidden" class="hiddenRecaptcha required" name="hiddenRecaptcha" id="hiddenRecaptcha"> Oct 30, 2015 at 19:50
  • 2
    @IvanJuarez This is a good question to ask as a new question. Oct 31, 2015 at 14:24
  • 5
    For those of you wanting to use grecaptcha.getResponse() with multiple instances you can simply reference each render as 0,1,2,etc. For example the first instance would be referenced as grecaptcha.getResponse(0).
    – Gene Kelly
    Feb 23, 2016 at 22:33
  • 2
    Save my time its perfect solution Dec 20, 2016 at 10:17
  • 8
    Wow! This took some working on, but to add to @GeneKelly's note regarding grecaptcha.getResponse(0) and grecaptcha.getResponse(1) to validate multiple instances I would add that the indexing does have to correspond with the grecaptcha.render ordering. For this example, grecaptcha.render('RecaptchaField1'... would be verified with grecaptcha.getResponse(0) and grecaptcha.render('RecaptchaField2'... would be verified with grecaptcha.getResponse(1), etc...
    – codacopia
    Nov 30, 2017 at 20:46
85

Simple and straightforward:

  1. Create your Recaptcha fields normally with this:

    <div class="g-recaptcha" data-sitekey="YOUR_KEY_HERE"></div>
    
  2. Load the script with this:

    <script src="https://www.google.com/recaptcha/api.js?onload=CaptchaCallback&render=explicit" async defer></script>
    
  3. Now call this to iterate over the fields and create the Recaptchas:

    <script type="text/javascript">
      var CaptchaCallback = function() {
        jQuery('.g-recaptcha').each(function(index, el) {
            grecaptcha.render(el, {
                'sitekey' : jQuery(el).attr('data-sitekey')
                ,'theme' : jQuery(el).attr('data-theme')
                ,'size' : jQuery(el).attr('data-size')
                ,'tabindex' : jQuery(el).attr('data-tabindex')
                ,'callback' : jQuery(el).attr('data-callback')
                ,'expired-callback' : jQuery(el).attr('data-expired-callback')
                ,'error-callback' : jQuery(el).attr('data-error-callback')
            });
        });
      };
    </script>
    
9
  • 4
    This is the right way IMO, this makes it dynamic in that I can have unlimited instances without defining static ids Oct 7, 2016 at 16:45
  • 1
    If you need to extract manually the captcha code (like in ajax requests) look my answer.
    – VanDir
    Jan 25, 2017 at 19:38
  • Could you please take a look in my question? The POST parameter is going empty. Apr 4, 2017 at 16:55
  • 3
    data-sitekey="YOUR_KEY_HERE" is useless and can be removed from the div (if you need to change the key, less places to edit)
    – the_nuts
    Jun 24, 2017 at 20:26
  • 3
    Actually you have a typo there. The Attribute data-sitekey is needen and makes this even more dynamic in case you would have multiple sitekeys, why ever. The correct line would be grecaptcha.render(el, {'sitekey' : $(el).attr('data-sitekey') });
    – Gkiokan
    Aug 24, 2017 at 11:05
16

This answer is an extension to @raphadko's answer.

If you need to extract manually the captcha code (like in ajax requests) you have to call:

grecaptcha.getResponse(widget_id)

But how can you retrieve the widget id parameter?

I use this definition of CaptchaCallback to store the widget id of each g-recaptcha box (as an HTML data attribute):

var CaptchaCallback = function() {
    jQuery('.g-recaptcha').each(function(index, el) {
        var widgetId = grecaptcha.render(el, {'sitekey' : 'your code'});
        jQuery(this).attr('data-widget-id', widgetId);
    });
};

Then I can call:

grecaptcha.getResponse(jQuery('#your_recaptcha_box_id').attr('data-widget-id'));

to extract the code.

3
  • 2
    Hey, thanks for that. What is the #your_recaptcha_box_id? Nov 24, 2019 at 3:48
  • This is working great for me in ajax, BUT- in a case where there is an error on the form (eg, user not found) - and a relevant error is returned from the ajax method, i ould usually resat the captcha by calling grecaptcha.reset(); which worked great in a single-form-per-page setup. But in a multiple form per page, this reset command does not effect/reset the captcha. I tried calling the CaptchaCallback function as well, but it did not solve the issue.
    – kneidels
    Nov 29, 2020 at 9:03
  • Answering my comment above. See: stackoverflow.com/questions/31344626/…
    – kneidels
    Nov 29, 2020 at 9:31
14

A similar question was asked about doing this on an ASP page (link) and the consensus over there was that it was not possible to do with recaptcha. It seems that multiple forms on a single page must share the captcha, unless you're willing to use a different captcha. If you are not locked into recaptcha a good library to take a look at is the Zend Frameworks Zend_Captcha component (link). It contains a few

2
  • 12
    It is possible with reCAPTCHA actually, just not possible to do without JavaScript. Most browsers should support JavaScript and the default ReCaptcha uses JavaScript anyway so this solution is good. Hüseyin Yağlı's answer explains the solution. reCAPTCHA documentation for this solution is at developers.google.com/recaptcha/docs/display#explicit_render. I don't know though what would need to be done to solve the problem without JavaScript support though.
    – Edward
    Apr 11, 2016 at 14:36
  • 1
    It's absolutely possible with reCAPTCHA actually Aug 14, 2019 at 0:12
13

This is easily accomplished with jQuery's clone() function.

So you must create two wrapper divs for the recaptcha. My first form's recaptcha div:

<div id="myrecap">
    <?php
        require_once('recaptchalib.php');
        $publickey = "XXXXXXXXXXX-XXXXXXXXXXX";
        echo recaptcha_get_html($publickey);
    ?>
</div>

The second form's div is empty (different ID). So mine is just:

<div id="myraterecap"></div>

Then the javascript is quite simple:

$(document).ready(function() {
    // Duplicate our reCapcha 
    $('#myraterecap').html($('#myrecap').clone(true,true));
});

Probably don't need the second parameter with a true value in clone(), but doesn't hurt to have it... The only issue with this method is if you are submitting your form via ajax, the problem is that you have two elements that have the same name and you must me a bit more clever with the way you capture that correct element's values (the two ids for reCaptcha elements are #recaptcha_response_field and #recaptcha_challenge_field just in case someone needs them)

4
  • You will encounter issues when you request for a new challenge as it will be updating only one recaptcha instance Jun 9, 2012 at 17:46
  • this does not work ... means only the original captcha( the first one called <div id="myrecap"> here )only will be refreshed , and other not refreshed
    – Jaffer
    Dec 1, 2012 at 19:24
  • Actually Oxi, your concern was addressed by my previous comment
    – Serj Sagan
    Dec 3, 2012 at 20:29
  • 1
    You're probably doing it wrong... how about a link to your code in jsfiddle.net Anyways, there is no longer a need to do any of this... you should be using Hüseyin Yağlı answer.
    – Serj Sagan
    Oct 12, 2015 at 16:16
9

I know this question is old but in case if anyone will look for it in the future. It is possible to have two captcha's on one page. Pink to documentation is here: https://developers.google.com/recaptcha/docs/display Example below is just a copy form doc and you dont have to specify different layouts.

<script type="text/javascript">
  var verifyCallback = function(response) {
    alert(response);
  };
  var widgetId1;
  var widgetId2;
  var onloadCallback = function() {
    // Renders the HTML element with id 'example1' as a reCAPTCHA widget.
    // The id of the reCAPTCHA widget is assigned to 'widgetId1'.
    widgetId1 = grecaptcha.render('example1', {
      'sitekey' : 'your_site_key',
      'theme' : 'light'
    });
    widgetId2 = grecaptcha.render(document.getElementById('example2'), {
      'sitekey' : 'your_site_key'
    });
    grecaptcha.render('example3', {
      'sitekey' : 'your_site_key',
      'callback' : verifyCallback,
      'theme' : 'dark'
    });
  };
</script>
8

The grecaptcha.getResponse() method accepts an optional "widget_id" parameter, and defaults to the first widget created if unspecified. A widget_id is returned from the grecaptcha.render() method for each widget created, it is not related to the attribute id of the reCAPTCHA container!!

Each reCAPTCHA has its own response data. You have to give the reCAPTCHA div an ID and pass it to the getResponse method:

e.g.

<div id="reCaptchaLogin"
     class="g-recaptcha required-entry"
     data-sitekey="<?php echo $this->helper('recaptcha')->getKey(); ?>"
     data-theme="<?php echo($this->helper('recaptcha')->getTheme()); ?>"
     style="transform:scale(0.82);-webkit-transform:scale(0.82);transform-origin:0 0;-webkit-transform-origin:0 0;">
</div>


<script type="text/javascript">
  var CaptchaCallback = function() {
    jQuery('.g-recaptcha').each(function(index, el) {
        grecaptcha.render(el, {
            'sitekey' : jQuery(el).attr('data-sitekey')
            ,'theme' : jQuery(el).attr('data-theme')
            ,'size' : jQuery(el).attr('data-size')
            ,'tabindex' : jQuery(el).attr('data-tabindex')
            ,'callback' : jQuery(el).attr('data-callback')
            ,'expired-callback' : jQuery(el).attr('data-expired-callback')
            ,'error-callback' : jQuery(el).attr('data-error-callback')
        });
    });
  };
</script>

<script src="https://www.google.com/recaptcha/api.js?onload=CaptchaCallback&render=explicit" async defer></script>

Access response:

var reCaptchaResponse = grecaptcha.getResponse(0);

or

var reCaptchaResponse = grecaptcha.getResponse(1);
1
  • 2
    Woah, thank you! Couldn't find such a thing in the official documentation. This way you could have multiple captchas, without having explicit rendering. I have a case where I have normal rendering everywhere around the site and I need explicit rendering only in one place - this solved my issue. Dec 10, 2021 at 12:45
4

I have contact form in footer that always displays and also some pages, like Create Account, can have captcha too, so it's dynamically and I'm using next way with jQuery:

html:

<div class="g-recaptcha" id="g-recaptcha"></div>

<div class="g-recaptcha" id="g-recaptcha-footer"></div>

javascript

<script src="https://www.google.com/recaptcha/api.js?onload=CaptchaCallback&render=explicit&hl=en"></script>
<script type="text/javascript">
  var CaptchaCallback = function(){        
      $('.g-recaptcha').each(function(){
        grecaptcha.render(this,{'sitekey' : 'your_site_key'});
      })
  };
</script>
0
4

This is a JQuery-free version of the answer provided by raphadko and noun.

1) Create your recaptcha fields normally with this:

<div class="g-recaptcha"></div>

2) Load the script with this:

<script src="https://www.google.com/recaptcha/api.js?onload=CaptchaCallback&render=explicit" async defer></script>

3) Now call this to iterate over the fields and create the recaptchas:

var CaptchaCallback = function() {
    var captchas = document.getElementsByClassName("g-recaptcha");
    for(var i = 0; i < captchas.length; i++) {
        grecaptcha.render(captchas[i], {'sitekey' : 'YOUR_KEY_HERE'});
    }
};
2

Looking at the source code of the page I took the reCaptcha part and changed the code a bit. Here's the code:

HTML:

<div class="tabs">
    <ul class="product-tabs">
        <li id="product_tabs_new" class="active"><a href="#">Detailed Description</a></li>
        <li id="product_tabs_what"><a href="#">Request Information</a></li>
        <li id="product_tabs_wha"><a href="#">Make Offer</a></li>
    </ul>
</div>

<div class="tab_content">
    <li class="wide">
        <div id="product_tabs_new_contents">
            <?php $_description = $this->getProduct()->getDescription(); ?>
            <?php if ($_description): ?>
                <div class="std">
                    <h2><?php echo $this->__('Details') ?></h2>
                    <?php echo $this->helper('catalog/output')->productAttribute($this->getProduct(), $_description, 'description') ?>
                </div>
            <?php endif; ?>
        </div>
    </li>

    <li class="wide">
        <label for="recaptcha">Captcha</label>
        <div id="more_info_recaptcha_box" class="input-box more_info_recaptcha_box"></div>
    </li>

    <li class="wide">
        <label for="recaptcha">Captcha</label>
        <div id="make_offer_recaptcha_box" class="input-box make_offer_recaptcha_box"></div>
    </li>
</div>

jQuery:

<script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>
<script type="text/javascript">
    jQuery(document).ready(function() {
        var recapExist = false;
      // Create our reCaptcha as needed
        jQuery('#product_tabs_what').click(function() {
            if(recapExist == false) {
                Recaptcha.create("<?php echo $publickey; ?>", "more_info_recaptcha_box");
                recapExist = "make_offer_recaptcha_box";
            } else if(recapExist == 'more_info_recaptcha_box') {
                Recaptcha.destroy(); // Don't really need this, but it's the proper way
                Recaptcha.create("<?php echo $publickey; ?>", "more_info_recaptcha_box");
                recapExist = "make_offer_recaptcha_box";
            }
        });
        jQuery('#product_tabs_wha').click(function() {
            if(recapExist == false) {
                Recaptcha.create("<?php echo $publickey; ?>", "make_offer_recaptcha_box");
                recapExist = "more_info_recaptcha_box";
            } else if(recapExist == 'make_offer_recaptcha_box') {
                Recaptcha.destroy(); // Don't really need this, but it's the proper way (I think :)
                Recaptcha.create("<?php echo $publickey; ?>", "make_offer_recaptcha_box");
                recapExist = "more_info_recaptcha_box";
            }
        });
    });
</script>

I am using here simple javascript tab functionality. So, didn't included that code.

When user would click on "Request Information" (#product_tabs_what) then JS will check if recapExist is false or has some value. If it has a value then this will call Recaptcha.destroy(); to destroy the old loaded reCaptcha and will recreate it for this tab. Otherwise this will just create a reCaptcha and will place into the #more_info_recaptcha_box div. Same as for "Make Offer" #product_tabs_wha tab.

2

var ReCaptchaCallback = function() {
    	 $('.g-recaptcha').each(function(){
    		var el = $(this);
    		grecaptcha.render(el.get(0), {'sitekey' : el.data("sitekey")});
    	 });  
        };
<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?onload=ReCaptchaCallback&render=explicit" async defer></script>


ReCaptcha 1
<div class="g-recaptcha" data-sitekey="6Lc8WQcUAAAAABQKSITdXbc6p9HISCQhZIJwm2Zw"></div>

ReCaptcha 2
<div class="g-recaptcha" data-sitekey="6Lc8WQcUAAAAABQKSITdXbc6p9HISCQhZIJwm2Zw"></div>

ReCaptcha 3
<div class="g-recaptcha" data-sitekey="6Lc8WQcUAAAAABQKSITdXbc6p9HISCQhZIJwm2Zw"></div>

3
  • where is the 2 forms part?
    – MeSo2
    Mar 13, 2020 at 15:14
  • sorry not getting you Mar 14, 2020 at 20:35
  • just put <div class="g-recaptcha" data-sitekey="your_site_key"></div> where u needed , in forms/divs Mar 14, 2020 at 20:43
2

To add a bit to raphadko's answer: since you have multiple captchas (on one page), you can't use the (universal) g-recaptcha-response POST parameter (because it holds only one captcha's response). Instead, you should use grecaptcha.getResponse(opt_widget_id) call for each captcha. Here's my code (provided each captcha is inside its form):

HTML:

<form ... />

<div id="RecaptchaField1"></div>

<div class="field">
  <input type="hidden" name="grecaptcha" id="grecaptcha" />
</div>

</form>

and

<script src="https://www.google.com/recaptcha/api.js?onload=CaptchaCallback&render=explicit" async defer></script>

JavaScript:

var CaptchaCallback = function(){
    var widgetId;

    $('[id^=RecaptchaField]').each(function(index, el) {

         widgetId = grecaptcha.render(el.id, {'sitekey' : 'your_site_key'});

         $(el).closest("form").submit(function( event ) {

            this.grecaptcha.value = "{\"" + index + "\" => \"" + grecaptcha.getResponse(widgetId) + "\"}"

         });
    });
};

Notice that I apply the event delegation (see refresh DOM after append element ) to all the dynamically modified elements. This binds every individual captha's response to its form submit event.

1
  • 1
    Wow, I was searching for hours for this. Thanks!!
    – Black
    Sep 12, 2018 at 15:01
1

A good option is to generate a recaptcha input for each form on the fly (I've done it with two but you could probably do three or more forms). I'm using jQuery, jQuery validation, and jQuery form plugin to post the form via AJAX, along with the Recaptcha AJAX API -

https://developers.google.com/recaptcha/docs/display#recaptcha_methods

When the user submits one of the forms:

  1. intercept the submission - I used jQuery Form Plugin's beforeSubmit property
  2. destroy any existing recaptcha inputs on the page - I used jQuery's $.empty() method and Recaptcha.destroy()
  3. call Recaptcha.create() to create a recaptcha field for the specific form
  4. return false.

Then, they can fill out the recaptcha and re-submit the form. If they decide to submit a different form instead, well, your code checks for existing recaptchas so you'll only have one recaptcha on the page at a time.

1

Here's a solution that builds off many of the excellent answers. This option is jQuery free, and dynamic, not requiring you to specifically target elements by id.

1) Add your reCAPTCHA markup as you normally would:

<div class="g-recaptcha" data-sitekey="YOUR_KEY_HERE"></div>

2) Add the following into the document. It will work in any browser that supports the querySelectorAll API

<script src="https://www.google.com/recaptcha/api.js?onload=renderRecaptchas&render=explicit" async defer></script>
<script>
    window.renderRecaptchas = function() {
        var recaptchas = document.querySelectorAll('.g-recaptcha');
        for (var i = 0; i < recaptchas.length; i++) {
            grecaptcha.render(recaptchas[i], {
                sitekey: recaptchas[i].getAttribute('data-sitekey')
            });
        }
    }
</script>
1

I would use invisible recaptcha. Then on your button use a tag like " formname='yourformname' " to specify which form is to be submitted and hide a submit form input.

The advantage of this is it allows for you to keep the html5 form validation intact, one recaptcha, but multiple button interfaces. Just capture the "captcha" input value for the token key generated by recaptcha.

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

<div class="g-recaptcha" data-sitekey="yours" data-callback="onSubmit" data-size="invisible"></div>
<script>
var formanme = ''
$('button').on('click', function () { formname = '#'+$(this).attr('formname');
    if ( $(formname)[0].checkValidity() == true) { grecaptcha.execute(); }
    else { $(formname).find('input[type="submit"]').click() }
    });

var onSubmit = function(token) {
    $(formname).append("<input type='hidden' name='captcha' value='"+token+"' />");
    $(formname).find('input[type="submit"]').click()
    };
</script>

I find this FAR simpler and easier to manage.

0

It is possible, just overwrite the Recaptcha Ajax callbacks. Working jsfiddle: http://jsfiddle.net/Vanit/Qu6kn/

You don't even need a proxy div because with the overwrites the DOM code won't execute. Call Recaptcha.reload() whenever you want to trigger the callbacks again.

function doSomething(challenge){
    $(':input[name=recaptcha_challenge_field]').val(challenge);
    $('img.recaptcha').attr('src', '//www.google.com/recaptcha/api/image?c='+challenge);
}

//Called on Recaptcha.reload()
Recaptcha.finish_reload = function(challenge,b,c){
    doSomething(challenge);
}

//Called on page load
Recaptcha.challenge_callback = function(){
    doSomething(RecaptchaState.challenge)
}

Recaptcha.create("YOUR_PUBLIC_KEY");
0

Here is a nice guide for doing exactly that:

http://mycodde.blogspot.com.ar/2014/12/multiple-recaptcha-demo-same-page.html

Basically you add some parameters to the api call and manually render each recaptcha:

<script src="https://www.google.com/recaptcha/api.js?onload=myCallBack&render=explicit" async defer></script>
<script>
        var recaptcha1;
        var recaptcha2;
        var myCallBack = function() {
            //Render the recaptcha1 on the element with ID "recaptcha1"
            recaptcha1 = grecaptcha.render('recaptcha1', {
                'sitekey' : '6Lc_0f4SAAAAAF9ZA', //Replace this with your Site key
                'theme' : 'light'
            });

            //Render the recaptcha2 on the element with ID "recaptcha2"
            recaptcha2 = grecaptcha.render('recaptcha2', {
                'sitekey' : '6Lc_0f4SAAAAAF9ZA', //Replace this with your Site key
                'theme' : 'dark'
            });
        };
</script>

PS: The "grecaptcha.render" method receives an ID

Not the answer you're looking for? Browse other questions tagged or ask your own question.