Merge remote-tracking branch 'upstream/master'
* upstream/master: don't autofocus external links like feedback fixup c57fef7b: always set lastFocusedViaClick notify embedder on closing colorpicker code cosmetics suppress focus outline when invoked via mouse
This commit is contained in:
commit
731e8ad249
35
js/dom.js
35
js/dom.js
|
@ -313,20 +313,31 @@ function initCollapsibles({bindClickOn = 'h2'} = {}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Makes the focus outline appear on keyboard tabbing, but not on mouse clicks.
|
||||||
function focusAccessibility() {
|
function focusAccessibility() {
|
||||||
// Makes the focus outline appear on keyboard tabbing, but not on mouse clicks.
|
// last event's focusedViaClick
|
||||||
// Since we don't want full layout recalc, we modify only the closest focusable element,
|
focusAccessibility.lastFocusedViaClick = false;
|
||||||
// which we try to find in DOM for this many parentElement jumps:
|
// tags of focusable elements;
|
||||||
const focusables = focusAccessibility.ELEMENTS =
|
// to avoid a full layout recalc we modify the closest one
|
||||||
['a', 'button', 'input', 'textarea', 'label', 'select', 'summary'];
|
focusAccessibility.ELEMENTS = [
|
||||||
|
'a',
|
||||||
|
'button',
|
||||||
|
'input',
|
||||||
|
'textarea',
|
||||||
|
'label',
|
||||||
|
'select',
|
||||||
|
'summary',
|
||||||
|
];
|
||||||
|
// try to find a focusable parent for this many parentElement jumps:
|
||||||
const GIVE_UP_DEPTH = 4;
|
const GIVE_UP_DEPTH = 4;
|
||||||
|
|
||||||
addEventListener('mousedown', suppressOutlineOnClick, {passive: true});
|
addEventListener('mousedown', suppressOutlineOnClick, {passive: true});
|
||||||
addEventListener('keydown', keepOutlineOnTab, {passive: true});
|
addEventListener('keydown', keepOutlineOnTab, {passive: true});
|
||||||
|
|
||||||
function suppressOutlineOnClick({target}) {
|
function suppressOutlineOnClick({target}) {
|
||||||
for (let el = target, i = 0; el && i++ < GIVE_UP_DEPTH; el = el.parentElement) {
|
for (let el = target, i = 0; el && i++ < GIVE_UP_DEPTH; el = el.parentElement) {
|
||||||
if (focusables.includes(el.localName)) {
|
if (focusAccessibility.ELEMENTS.includes(el.localName)) {
|
||||||
|
focusAccessibility.lastFocusedViaClick = true;
|
||||||
if (el.dataset.focusedViaClick === undefined) {
|
if (el.dataset.focusedViaClick === undefined) {
|
||||||
el.dataset.focusedViaClick = '';
|
el.dataset.focusedViaClick = '';
|
||||||
}
|
}
|
||||||
|
@ -337,13 +348,14 @@ function focusAccessibility() {
|
||||||
|
|
||||||
function keepOutlineOnTab(event) {
|
function keepOutlineOnTab(event) {
|
||||||
if (event.which === 9) {
|
if (event.which === 9) {
|
||||||
|
focusAccessibility.lastFocusedViaClick = false;
|
||||||
setTimeout(keepOutlineOnTab, 0, true);
|
setTimeout(keepOutlineOnTab, 0, true);
|
||||||
return;
|
return;
|
||||||
} else if (event !== true) {
|
} else if (event !== true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let el = document.activeElement;
|
let el = document.activeElement;
|
||||||
if (!el || !focusables.includes(el.localName)) {
|
if (!el || !focusAccessibility.ELEMENTS.includes(el.localName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (el.dataset.focusedViaClick !== undefined) {
|
if (el.dataset.focusedViaClick !== undefined) {
|
||||||
|
@ -360,18 +372,23 @@ function focusAccessibility() {
|
||||||
* Switches to the next/previous keyboard-focusable element
|
* Switches to the next/previous keyboard-focusable element
|
||||||
* @param {HTMLElement} rootElement
|
* @param {HTMLElement} rootElement
|
||||||
* @param {Number} step - for exmaple 1 or -1
|
* @param {Number} step - for exmaple 1 or -1
|
||||||
|
* @returns {HTMLElement|false|undefined} -
|
||||||
|
* HTMLElement: focus changed,
|
||||||
|
* false: focus unchanged,
|
||||||
|
* undefined: nothing to focus
|
||||||
*/
|
*/
|
||||||
function moveFocus(rootElement, step) {
|
function moveFocus(rootElement, step) {
|
||||||
const elements = [...rootElement.getElementsByTagName('*')];
|
const elements = [...rootElement.getElementsByTagName('*')];
|
||||||
const activeIndex = Math.max(0, elements.indexOf(document.activeElement));
|
const activeIndex = Math.max(0, elements.indexOf(document.activeElement));
|
||||||
const num = elements.length;
|
const num = elements.length;
|
||||||
|
const {activeElement} = document;
|
||||||
for (let i = 1; i < num; i++) {
|
for (let i = 1; i < num; i++) {
|
||||||
const elementIndex = (activeIndex + i * step + num) % num;
|
const elementIndex = (activeIndex + i * step + num) % num;
|
||||||
// we don't use positive tabindex so we stop at any valid value
|
// we don't use positive tabindex so we stop at any valid value
|
||||||
const el = elements[elementIndex];
|
const el = elements[elementIndex];
|
||||||
if (!el.disabled && el.tabIndex >= 0) {
|
if (!el.disabled && el.tabIndex >= 0) {
|
||||||
el.focus();
|
el.focus();
|
||||||
return;
|
return activeElement !== el && el;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,12 @@ function messageBox({
|
||||||
document.body.appendChild(messageBox.element);
|
document.body.appendChild(messageBox.element);
|
||||||
|
|
||||||
messageBox.originalFocus = document.activeElement;
|
messageBox.originalFocus = document.activeElement;
|
||||||
moveFocus(messageBox.element, 1);
|
// skip external links like feedback
|
||||||
|
while ((moveFocus(messageBox.element, 1) || {}).target === '_blank') {/*NOP*/}
|
||||||
|
// suppress focus outline when invoked via click
|
||||||
|
if (focusAccessibility.lastFocusedViaClick && document.activeElement) {
|
||||||
|
document.activeElement.dataset.focusedViaClick = '';
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof onshow === 'function') {
|
if (typeof onshow === 'function') {
|
||||||
onshow(messageBox.element);
|
onshow(messageBox.element);
|
||||||
|
|
|
@ -222,11 +222,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hide({notify = true} = {}) {
|
function hide() {
|
||||||
if (shown) {
|
if (shown) {
|
||||||
if (notify) {
|
colorpickerCallback('');
|
||||||
colorpickerCallback('');
|
|
||||||
}
|
|
||||||
unregisterEvents();
|
unregisterEvents();
|
||||||
focusNoScroll(prevFocusedElement);
|
focusNoScroll(prevFocusedElement);
|
||||||
$root.remove();
|
$root.remove();
|
||||||
|
@ -623,7 +621,7 @@
|
||||||
case 27:
|
case 27:
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
hide({notify: false});
|
hide();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -643,17 +641,20 @@
|
||||||
//region Event utilities
|
//region Event utilities
|
||||||
|
|
||||||
function colorpickerCallback(colorString = currentColorToString()) {
|
function colorpickerCallback(colorString = currentColorToString()) {
|
||||||
// Esc pressed?
|
const isCallable = typeof options.callback === 'function';
|
||||||
if (!colorString) {
|
// hiding
|
||||||
|
if (!colorString && isCallable) {
|
||||||
options.callback('');
|
options.callback('');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
userActivity &&
|
userActivity &&
|
||||||
$inputs[currentFormat].every(el => el.checkValidity()) &&
|
$inputs[currentFormat].every(el => el.checkValidity())
|
||||||
typeof options.callback === 'function'
|
|
||||||
) {
|
) {
|
||||||
lastOutputColor = colorString.replace(/\b0\./g, '.');
|
lastOutputColor = colorString.replace(/\b0\./g, '.');
|
||||||
options.callback(lastOutputColor);
|
if (isCallable) {
|
||||||
|
options.callback(lastOutputColor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user