How to keyboard navigate to reCAPTCHA in a modal dialog? - dojo

I need to open Google's latest reCAPTCHA widget in a popup (modal) dialog, a Dojo Dialog in my case, and I've got that working fine, but I just realized that the user cannot keyboard navigate to it.
When the reCAPTCHA widget is displayed in the main view, not a modal dialog, then of course the user can easily keyboard navigate to it.
Has anyone found a way to set focus on the reCAPTCHA widget so that the user can access it without a mouse when the reCAPTCHA is in a Dojo Dialog?
I did see that reCAPTCHA is generated within an <iframe>. Is that part of the hurdle - that keyboard navigation can't reach content within an iframe? I've even tried to call document.getElementById("recaptcha-anchor") since I saw that that's the id of the <span> that holds the "checkbox" - but that is returning null. How to reach an element within an iframe?
I have a jsfiddle example available for demonstration at
https://jsfiddle.net/gregorco/xqs8w5pm/5/
<script>
var onloadCaptchaCallback = function() {
console.log("jsfiddle: rendering captcha");
globalRecaptchaWidgetId = grecaptcha.render('captchaDiv', {
'sitekey' : '6LcgSAMTAAAAACc2C7rc6HB9ZmEX4SyB0bbAJvTG',
'callback' : verifyCaptchaCallback,
'tabindex' : 2
});
grecaptcha.reset();
}
var verifyCaptchaCallback = function(g_recaptcha_response) {
console.log("Response validated. Not a robot.");
};
</script>
<script src='https://www.google.com/recaptcha/api.js?onload=onloadCaptchaCallback&render=explicit' async defer></script>
<div id="testDiv">
<button type="dojo/form/Button" onClick="captchaPopup.show();">Open reCAPTCHA</button>
</div>
<div data-dojo-type="dijit/Dialog" data-dojo-id="captchaPopup" title="Human Verification" style="width:350px;">
Cannot keyboard navigate to the checkbox!
<table>
<tr>
<td>
<div id="captchaDiv"></div><br/>
</td>
</tr>
</table>
</div>

Give this fiddle a try. Normally Dijit dialogs don't work too well with iframes in them because it doesn't know how to parse the content inside an iframe. In this case, we can use some of Dojo's functions to work around it. One notable thing to point out is that I've disabled autofocus of the Dijit Dialog so that it won't automatically focus the closeNode inside the dialog.
After the dialog loads, tab>space will select the captcha.

This may help others facing similar issue, but with Bootstrap modal dialog. I found the following solution on GitHub. Add the following Javascript to override Bootstrap:
Bootstrap 3x
$.fn.modal.Constructor.prototype.enforceFocus = function () { };
Bootstrap 4x
$.fn.modal.Constructor.prototype._enforceFocus = function () { };

Related

How to disable blur call on the active element from SwiperJS in onTouchStart handler?

Is it possible to disable this blur call on the active element from SwiperJS in the onTouchStart event handler?
Some background:
For touch and desktop devices I'm using swiper for forms on swiper-slides. Within a form I'm using vue-select (a combobox).
The Problem: When the user selects an entry, the entry get not selected on the first time but on the second time.
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide">
<div>First form</div>
<v-select :options="selectionEntries"></v-select>
</div>
<div class="swiper-slide">
<div>Second form</div>
<v-select :options="selectionEntries"></v-select>
</div>
</div>
</div>
See also this example on codepen
I figured out that it seems to work correctly:
When I remove the blur-listener on the input field of the vue-select box. But it is used to close the selection list when the user leaves the field.
When I comment out this blur call in SwiperJS. I'm not sure why it is used there.
The first point is not an option, so is it possible to disable the blur call of SwiperJS via configuration?
Currently I'm using this workaround (SwiperJS V6.4.1):
const swiper = new Swiper(".swiper-container", {
// Workaround part 1:
touchStartPreventDefault: false
})
// Workaround part 2:
swiper.touchEventsData.formElements = 'notExistingHtmlTagName'
Part 1: To handle mouse down and click events on all elements, set the swiper parameter touchStartPreventDefault: false.
That will disable this code block: https://github.com/nolimits4web/swiper/blob/9dead9ef4ba5d05adf266deb7e3703ceb199a241/src/components/core/events/onTouchStart.js#L90-L97
Part 2: Set swiper.touchEventsData.formElements = 'undefined' to define nothing as formElements. That will disable the code block that calls blur: https://github.com/nolimits4web/swiper/blob/9dead9ef4ba5d05adf266deb7e3703ceb199a241/src/components/core/events/onTouchStart.js#L81-L88

XPages - Bootstrap popover

I have an icon, which when you hover, pops up some extra information in a bootstrap popover
This works as expected, however, if I then click on any field on the page, which then does a partial refresh of a div containing the icon, it then loses the hover functionality.
Icon code:
<!--INFO BUTTON START-->
<xp:text escape="false" id="computedField4">
<xp:this.value><![CDATA[#{javascript:try{
var text = #DbLookup(#DbName(), "LookupKeywordLists", "Info"+compositeData.fieldName, "Members");
return " <i class='fa fa-info-circle' data-container='body' data-toggle='popover' data-trigger='hover' data-placement='right' data-content='"+text+"'></i>"
}catch(e){
openLogBean.addError(e,this.getParent());
}
}]]></xp:this.value>
<xp:this.rendered><![CDATA[#{javascript:try{
return compositeData.showInfoIcon;
}catch(e){
openLogBean.addError(e,this.getParent());
}}]]></xp:this.rendered>
</xp:text>
<!--INFO BUTTON END-->
Script block on the page:
<xp:scriptBlock id="scriptBlock1">
<xp:this.value><![CDATA[$(document).ready(function(){
$('[data-toggle="popover"]').popover({
trigger: 'hover',
title: 'Information'
});
});]]></xp:this.value>
</xp:scriptBlock>
The script block is currently outside the div that the partial refresh "refreshes" however I tried putting it within the div, which didn't resolve the issue. Any ideas? Thanks
You need to add the popover when the partial refresh occurs. In order to do so you use Dojo to subscribe to the partialrefresh-complete event.
This answer can help you: https://stackoverflow.com/a/49014247/785061.

Am I doing this data-menu-top properly? Do I need to change my layout entirely to get this to work?

I'm using data-menu-top on this page because everything is fixed and uses Skrollr to animate the different sections into view. The reason everything is fixed is so that I could do full-page SVGs that cover the height of the page (if you think there's a better way to do this, I would love to be enlightened).
Here's a link to the project development page: http://pman.mindevo.com
The button that appears on the first section has data-menu-top="10300", and this works great on Chrome, but when I try to view it in Firefox (33.0) the link doesn't do anything at all.
I am initializing using this code:
<script type="text/javascript">
setTimeout(function() {
var s = skrollr.init({
});
skrollr.menu.init(s, {
easing: 'quadratic',
duration: function(currentTop, targetTop) {
return 1500;
}
});
}, 1000);
</script>
Am I properly using data-menu-top? Is this a bug I'm not aware of using fixed layouts that are hidden using height?
Do I need to change the layout somehow to accomplish what I want and have it work in Firefox?
So the problem with Firefox was the way that it handles <button> linking. Here's the way the button was in the HTML:
<button class="buy buypotato">
<a data-menu-top="10300" href="#potatoPurchase1" class="purchase-options first-popup-link">
<svg ....etc></svg>
</button>
In Firefox it wasn't doing anything upon clicking, and got me thinking perhaps I'm using "button" HTML element incorrectly. Anyways, changing it to a div like so:
<div class="buy buypotato">
<a data-menu-top="10300" href="#potatoPurchase1" class="purchase-options first-popup-link">
<svg ....etc></svg>
</div>
That allowed Firefox to utilize Skrollr-menu to scroll to where I needed it to.
There might be a better way to do the layout on this, I'm still experimenting.

durandaljs 2.0 navigation

I have a view with pagination. When the user clicks on a page number, I display the data for that page.
I only want the data items to be replaced so I don't want to navigate to the "next page." So what I'm doing is using the router.navigate(url, { replace: false, trigger: false }); to add the page to the browser's history, but not to trigger the navigate there.
If after I get the data, I click on the browser's back button, the URL changes to the previous one, but I don't get an event. If once I'm back in the previous page, I click the browser's forward button, I get the trigger event from that page.
Example. I'm at /# and it is displaying page 1 of the data. The user click on the "next page" link on the page. I display page 2's data, and I replace the url with /#welcome/2 Now if I click on the browser's back button the URL changes back to /# but the page doesn't trigger. If I press the browser's forward button the URL changes back to /#welcome/2 and the page triggers. Now that that has happened, I can click the back button and page one will trigger; and I can go back and forth between page 1 and page 2. If the user clicks on "page 3", the problem happens again.
If you all need a working example, I will deploy it, but currently this is only running on my local box.
IMO paging, like sorting/filtering represent the internal state of a view/widget and shouldn't be presented via routes. Consider e.g. user is on #something/3 and bookmarks the url. After deleting a couple of items there's no #something/3 any longer and the bookmark fails. Here's some more thought food on that topic http://lostechies.com/derickbailey/2011/08/03/stop-using-backbone-as-if-it-were-a-stateless-web-server/
Update based on comments:
activate on hitting browser back get's not called for two reason. a) welcome.js returns a singleton and b) in shell.html viewCache is set to true. When the user press browser forward it get's called because at this time route #welcome/2 from the SPA perspective is called the first time, so activate kicks in.
One way to make the system work would be to force every page change (regardless if it was initialized by SPA or browser) running through activate. Here are the required steps: Convert the singleton into a constructor, set cacheViews: false and replace click events by normal hrefs that calls the page route.
Update 2
Here's an example that combines the inPage navigation (without router involvement) with the ability to use browser back/forth navigation. init is responsible for setting up things that are common for activate and gotoPage.
Viewmodel
define(['plugins/router', 'knockout'], function( router, ko ) {
var ctor = function() {
this.pageNo = ko.observable();
this.pageData = ko.observable();
};
ctor.prototype.activate = function( page ) {
this.init(page);
};
ctor.prototype.init = function( page ) {
this.pageNo(page || 1);
this.pageData('Data for ' + this.pageNo());
};
ctor.prototype.gotoPage = function( page ) {
var url = "extras/welcome/" + page;
this.init(page);
router.navigate(url, { replace: false, trigger: false });
};
return ctor;
});
View
<section>
<h1>
Hello Durandal Pagination
</h1>
<a data-bind="click: gotoPage.bind($data, 1)" style="cursor: pointer;">Page 1</a>
<a data-bind="click: gotoPage.bind($data, 2)" style="cursor: pointer;">Page 2</a>
<a data-bind="click: gotoPage.bind($data, 3)" style="cursor: pointer;">Page 3</a>
<h2 data-bind="text: pageData"></h2>
</section>
Live example at: http://dfiddle.github.io/dFiddle-2.0/#extras/welcome

WIndows 8 Metro List View Event Listener

I am trying to create a simple HTML Metro App for Windows 8. I want to display a list view, and based on the clicked item display different content on the screen. It sounds trivial, right?
But it doesn't work! Here is my code:
<div id="frameListViewTemplate" data-win-control="WinJS.Binding.Template">
<img data-win-bind="src: picture" class="thumbnail" />
</div>
<div id="basicListView" data-win-control="WinJS.UI.ListView"
data-win-options="{itemDataSource : DataExample.itemList.dataSource, itemTemplate: select('#frameListViewTemplate'),onselectionchanged : handler}">
</div>
Than in the defult.js
var myListView = document.getElementById("basicListView").winControl;
myListView.addEventListener("selectionchanged", handler);
And the handler:
function handler() {
console.log("Inside the handler : ");
}
handler.supportedForProcessing = true;
So the handler is never called. My questions are: How can I add an event listener and its handler to the listview control.
How can I recognize which element on the list view was clicked.
P.S.
The listview is displayed properly in my app.
Thank you for help,
J
To get the item that is "clicked", you need to use itemInvoked. Selection changed would happen when the user cross slides on the item to select it, rather than taping/clicking to "invoke" it.
http://msdn.microsoft.com/en-us/library/windows/apps/br211827.aspx has some basic details.