I have the app.js file where gmarkers is an array of objects :
var vm = new Vue({
el: '#lili',
data: {
listings: gmarkers
}
});
In my HTML file :
<div id="lili" >
<div
v-repeat="gmarker : listings"
class="listing listing-list col-sm-12"
style="height:50px;line-height:50px;"
data-cat="#{{gmarker.mycategory}}"
data-id="#{{gmarker.id}}"
>
<span style="color:black;">
<a target="_blank" href="#{{gmarker.listlink}}">
#{{ gmarker.listname }}
</a>
</span>
<span class="tag blue-back" style="margin-left:5px;">
<a target="_blank" href="#{{gmarker.catlink}}">
#{{gmarker.listcat}}
</a>
</span>
<span style="margin-left:20px;color:#bbb;">
#{{gmarker.distance}}km
</span>
</div>
</div>
When loading the page, the DOM loads correctly, displaying the list I'm looking for but when (after an ajax call in js) the gmarkers object changes, the DOM doesn't change.
The new gmarkers is totally created from scratch and containing new objects.
What am I missing ?
It sound like you are wrong about what data is being bound here. You are expecting data: {listings: gmarkers} to do binding between vm.listings and the gmarkers array. This isn't what it does. It copies the gmarkers data into the listings and doesn't listen to gmarkers anymore. See this fiddle for an example of what I think you are doing wrong.
We can fix the above fiddle by setting the vm.listings to the new array of objects after the ajax request.
vm.$set('listings', gmarkers);
You can see this in action in this updated fiddle.
It is actually pretty common to see a blank listings until ajax returns them. Here is some sample code that will call an ajax request when vue is ready and update the listings once it is finished. Note: this exampe uses vue-resources to perform the ajax request. You may not have this. You can always just use jQuery or javascript to perform this.
data: {
listings: []
},
ready: function () {
this.fetchListings();
},
methods: {
fetchListings: function () {
this.$http.get('/your/api/listings', function (response) {
this.$set('listings', response);
});
}
}
This fiddle shows the above code but pay attention to the differences required for the fake ajax request I used.
I am building an application that uses the Bootstrap Collapse component to render a sequence of panels, all of which will initially be in the collapsed state.
Since the page may contain many such panels and each of them may contain a large amount of content, it seems appropriate to populate these panels on demand, by executing an AJAX call when the user expands any panel.
The dynamic content of the page (including the markup for the panels) is rendered using AngularJS, and I assume it's possible to configure Angular to bind to an event on the panel elements, that results in their content being lazy loaded when they expand.
Unfortunately, after looking at the AngularJS docs and the available tutorials, I can't see how best to tackle this. Can anyone throw any light on it?
Thanks in advance,
Tim
This is way old, but the question might still come up now and then. I now find this to be the most suitable solution without polluting your controllers:
(myDirective loading its content via AJAX right after its creation.)
<accordion>
<accordion-group
heading=""
ng-repeat="foo in bar"
ng-init="status = {load: false}"
ng-click="status.load = true">
<myDirective ng-if="status.load"></myDirective>
</accordion-group>
</accordion>
each element created by ng-repeat gets its own $scope, so clicking ab accordion-group will result in only the respective directive being loaded.
edit:
depending on latency and the size of the data that's to be lazy loaded, you might consider using ng-mouseover instead of ng-click. That way loading starts some 100ms before the user opens the accordion which can reduce 'sluggishness' of your UI. Obviously there's the downside of occasionally loading content of groups that are never actually clicked.
#Tim Coulter, I've created something following the idea of #Stewie.
It can definitely be improved, but I guess it's a good starting point.
I've created a small directive to bind the click event of the accordion's panel. When the click event is fired, I passed the panel template via the panel-template= attribute and it updates the main-template which is used inside the panel.
It makes reference to 2 html files (panel1.html and panel2.html) that contains the content of the each panel.
I would recommend to create a service to fetch these files via AJAX - just the way you wanted.
On the code below I created a service called dataService for this purpose and you should bind it to the click event - so files are loaded on demand when the user clicks on it.
Note the the mainTemplate is a common panel to all accordions, so when it changes the all the accordions will have the same content, BUT I am assuming you want to display only one panel at time, right ?!
Anyway as I said before the logic can be improved to fix these little 'gotchas', but I believe the core functionality is there to start with. :)
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
<script>
var myApp = angular.module('myApp', []);
myApp.controller('AccordionDemoCtrl', ['$scope', 'dataService', function ($scope, dataService) {
$scope.oneAtATime = true;
$scope.mainTemplate = '';
$scope.groups = [
{
id: "001",
title: "Dynamic Group Header - 1",
content: "Dynamic Group Body - 1",
template: "panel1.html"
},
{
id: "002",
title: "Dynamic Group Header - 2",
content: "Dynamic Group Body - 2",
template: "panel2.html"
}
];
}]);
myApp.factory('dataService', [ '$http', function($http){
return {
getData: function() {
return // you AJAX content data here;
}
}
}]);
myApp.directive('accordionToggle', [function () {
return {
restrict: 'C',
scope: {
mainTemplate: '=',
panelTemplate: '#'
},
link: function (scope, element, iAttrs) {
element.bind('click', function(e){
scope.mainTemplate = scope.panelTemplate;
scope.$apply();
});
}
};
}]);
</script>
</head>
<body ng-controller="AccordionDemoCtrl">
<div class="accordion" id="accordionParent">
<div class="accordion-group" ng-repeat="group in groups" >
<div class="accordion-heading">
<a class="accordion-toggle" main-template="$parent.mainTemplate" panel-template="{{ group.template }}" data-toggle="collapse" data-parent="#accordionParent" href="#collapse{{ $parent.group.id }}">
Collapsible Group Item {{ $parent.group.id }}
</a>
</div>
<div id="collapse{{ group.id }}" class="accordion-body collapse">
<div class="accordion-inner">
<div class="include-example" ng-include="mainTemplate"></div>
</div>
</div>
</div>
</div>
</body>
</html>
EDIT italics = more detailed explanation added to the question. Thanks.
I'm building a jQuery Mobile site which has a Gallery section.
The gallery has a series of thumbnails on the top of the screen.
Users click on the thumbnail to load in new content, that being a larger image, text, and potentially audio on some of them.
It's at this point that I'm not sure what to do: the way jQuery Mobile works, it's geared towards loading new pages, or views. But I just want to inject new content in a container on the current page.
To be clear, when the user clicks on another thumbnail, a new image replaces the content of the container with new content.
I have two questions:
I'm not sure how to structure the dynamic content. I was thinking i'd create an html file for each item, which as a rule always contains a title, information and sometimes, audio.
I'm not sure how to script this functionality in jQuery Mobile. It's obviously Ajax, but I'm not familiar with it yet, especially since jQuery Mobile has it's own methods in place already which seems to redefine behaviors in a way that's contradictory to this approach described here.
Here is a code explanation of what i'm trying to do:
<!-- Galleries -->
<div data-role="page" id="galleries">
<div data-role="content" role="main">
This is the Selection UI, if i click on thumb2.jpg, it'd
fill #content-holder with the whatever html is in content2.php
<div id="thumb-carousel">
<img src="thumb1.jpg">
<img src="thumb2.jpg">
<img src="thumb3.jpg">
<img src="thumb4.jpg">
<img src="thumb5.jpg">
<img src="thumb6.jpg">
<img src="thumb7.jpg">
<img src="thumb8.jpg">
<img src="thumb9.jpg">
</div>
<!-- This is the container, currently it's filled
with the kinda stuff i need to put in it. -->
<div id="content-holder">
<img src="myimage1.jpg"/>
<p>Artwork Title</p>
<p>Caption</p>
<audio>//mp3</audio>
</div>
</div>
</div>
//remember to use event delegation because you never know when the page will be in the DOM
$(document).delegate('#galleries', 'pageinit', function () {
//bind a `click` event handler to each thumbnail link
$('#thumb-carousel').children().bind('click', function () {
$.ajax({
url : $(this).attr('href'),
success : function (serverResponse) {
//select the container,
//then fade it out,
//change it's HTML to the response from the AJAX request,
//and fade it back in
$('#content-holder').fadeOut(500, function () {
$(this).html(serverResponse).fadeIn(500);
});
},
error : function (jqXHR, textStatus, errorThrown) {
//remember to handle errors so your page doesn't seem broken to the user
}
});
//prevent the default behavior of the link, which is to navigate to the `href` attribute
return false;
});
});
This expects your server-response to be valid HTML markup that is ready to inject into the DOM, meaning no <html> or <body> tags, just what you want to add to the DOM:
<img src="..." />
<span>TITLE</span>
<audio src="..."></audio>
Here are some docs for ya:
$.ajax(): http://api.jquery.com/jquery.ajax
.closest(): http://api.jquery.com/closest
.fadeIn(): http://api.jquery.com/fadein
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?
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().
Simple and straightforward:
Create your Recaptcha fields normally with this:
<div class="g-recaptcha" data-sitekey="YOUR_KEY_HERE"></div>
Load the script with this:
<script src="https://www.google.com/recaptcha/api.js?onload=CaptchaCallback&render=explicit" async defer></script>
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>
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.
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
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)
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>
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);
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>
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'});
}
};
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">Detailed Description</li>
<li id="product_tabs_what">Request Information</li>
<li id="product_tabs_wha">Make Offer</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.
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>
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.
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:
intercept the submission - I used jQuery Form Plugin's beforeSubmit property
destroy any existing recaptcha inputs on the page - I used jQuery's $.empty() method and Recaptcha.destroy()
call Recaptcha.create() to create a recaptcha field for the specific form
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.
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>
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");
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
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.