mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-05-04 08:01:34 -04:00
Fix option selection for keyboard users
Keyboard users select a time slot by moving to the radio for that slot, using the arrow keys, and selecting it by pressing 'space' or 'enter', like a `<select>`. We allow this by listening for 'keydown' events from the 'enter' or 'space' keys on time slot radios that are checked. Browsers fire 'click' events alongside the 'keydown' event meaning it's possible for the code that makes the selection to be run twice. We currently guard against this by checking for the `pageX` property of the event object, reasoning that a click event fired by a key press won't have a cursor position. Most browsers we support set it to `0` but it isn't always the case: https://dom-event-test.glitch.me/results.html For those browsers, the `!event.pageX` condition resolves correctly so this works. Safari and versions of Internet Explorer before 11 however, set it to a positive number. In those browsers, moving the selection between radios using the arrow keys fired a 'click' event which, in Safari and IE<11, was treated as a mouse/touch event and so confirmed the selection. This made it impossible to select a later time. These changes replace the 'click' event on time slots with an artifical one that tracks mouse/trackpad clicks by listening for a 'mousedown' followed by a 'mouseup' on a time slot. This doesn't fire on key presses so avoids the problem.
This commit is contained in:
@@ -82,12 +82,35 @@
|
||||
});
|
||||
let categories = $component.data('categories').split(',');
|
||||
let name = $component.find('input').eq(0).attr('name');
|
||||
let reset = () => {
|
||||
let mousedownOption = null;
|
||||
const reset = () => {
|
||||
render('initial', {
|
||||
'categories': categories,
|
||||
'name': name
|
||||
});
|
||||
};
|
||||
const selectOption = (value) => {
|
||||
render('chosen', {
|
||||
'choices': choices.filter(
|
||||
element => element.value == value
|
||||
),
|
||||
'name': name
|
||||
});
|
||||
focusSelected();
|
||||
};
|
||||
const trackMouseup = (event) => {
|
||||
const parentNode = event.target.parentNode;
|
||||
|
||||
if (parentNode === mousedownOption) {
|
||||
const value = $('input', parentNode).attr('value');
|
||||
|
||||
selectOption(value);
|
||||
|
||||
// clear tracking
|
||||
mousedownOption = null;
|
||||
$(document).off('mouseup', trackMouseup);
|
||||
}
|
||||
};
|
||||
|
||||
$component
|
||||
.on('click', '.js-category-button', function(event) {
|
||||
@@ -104,38 +127,23 @@
|
||||
focusSelected();
|
||||
|
||||
})
|
||||
.on('click', '.js-option', function(event) {
|
||||
|
||||
// stop click being triggered by keyboard events
|
||||
if (!event.pageX) return true;
|
||||
|
||||
event.preventDefault();
|
||||
let value = $('input', this).attr('value');
|
||||
render('chosen', {
|
||||
'choices': choices.filter(
|
||||
element => element.value == value
|
||||
),
|
||||
'name': name
|
||||
});
|
||||
focusSelected();
|
||||
.on('mousedown', '.js-option', function(event) {
|
||||
mousedownOption = this;
|
||||
|
||||
// mouseup on the same option completes the click action
|
||||
$(document).on('mouseup', trackMouseup);
|
||||
})
|
||||
// space and enter, clicked on a radio confirm that option was selected
|
||||
.on('keydown', 'input[type=radio]', function(event) {
|
||||
|
||||
// intercept keypresses which aren’t enter or space
|
||||
// allow keypresses which aren’t enter or space through
|
||||
if (event.which !== 13 && event.which !== 32) {
|
||||
return true;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
let value = $(this).attr('value');
|
||||
render('chosen', {
|
||||
'choices': choices.filter(
|
||||
element => element.value == value
|
||||
),
|
||||
'name': name
|
||||
});
|
||||
focusSelected();
|
||||
selectOption(value);
|
||||
|
||||
})
|
||||
.on('click', '.js-done-button', function(event) {
|
||||
|
||||
Reference in New Issue
Block a user