diff --git a/js/dom.js b/js/dom.js index 1034542b..c956d729 100644 --- a/js/dom.js +++ b/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() { - // Makes the focus outline appear on keyboard tabbing, but not on mouse clicks. - // Since we don't want full layout recalc, we modify only the closest focusable element, - // which we try to find in DOM for this many parentElement jumps: - const focusables = focusAccessibility.ELEMENTS = - ['a', 'button', 'input', 'textarea', 'label', 'select', 'summary']; + // last event's focusedViaClick + focusAccessibility.lastFocusedViaClick = false; + // tags of focusable elements; + // to avoid a full layout recalc we modify the closest one + 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; + addEventListener('mousedown', suppressOutlineOnClick, {passive: true}); addEventListener('keydown', keepOutlineOnTab, {passive: true}); function suppressOutlineOnClick({target}) { 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) { el.dataset.focusedViaClick = ''; } @@ -337,13 +348,14 @@ function focusAccessibility() { function keepOutlineOnTab(event) { if (event.which === 9) { + focusAccessibility.lastFocusedViaClick = false; setTimeout(keepOutlineOnTab, 0, true); return; } else if (event !== true) { return; } let el = document.activeElement; - if (!el || !focusables.includes(el.localName)) { + if (!el || !focusAccessibility.ELEMENTS.includes(el.localName)) { return; } if (el.dataset.focusedViaClick !== undefined) { @@ -360,18 +372,23 @@ function focusAccessibility() { * Switches to the next/previous keyboard-focusable element * @param {HTMLElement} rootElement * @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) { const elements = [...rootElement.getElementsByTagName('*')]; const activeIndex = Math.max(0, elements.indexOf(document.activeElement)); const num = elements.length; + const {activeElement} = document; for (let i = 1; i < num; i++) { const elementIndex = (activeIndex + i * step + num) % num; // we don't use positive tabindex so we stop at any valid value const el = elements[elementIndex]; if (!el.disabled && el.tabIndex >= 0) { el.focus(); - return; + return activeElement !== el && el; } } } diff --git a/msgbox/msgbox.js b/msgbox/msgbox.js index 7a6ba81e..9d04c143 100644 --- a/msgbox/msgbox.js +++ b/msgbox/msgbox.js @@ -34,7 +34,12 @@ function messageBox({ document.body.appendChild(messageBox.element); 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') { onshow(messageBox.element); diff --git a/vendor-overwrites/colorpicker/colorpicker.js b/vendor-overwrites/colorpicker/colorpicker.js index 2617b5e9..38f4af75 100644 --- a/vendor-overwrites/colorpicker/colorpicker.js +++ b/vendor-overwrites/colorpicker/colorpicker.js @@ -222,11 +222,9 @@ } } - function hide({notify = true} = {}) { + function hide() { if (shown) { - if (notify) { - colorpickerCallback(''); - } + colorpickerCallback(''); unregisterEvents(); focusNoScroll(prevFocusedElement); $root.remove(); @@ -623,7 +621,7 @@ case 27: e.preventDefault(); e.stopPropagation(); - hide({notify: false}); + hide(); break; } } @@ -643,17 +641,20 @@ //region Event utilities function colorpickerCallback(colorString = currentColorToString()) { - // Esc pressed? - if (!colorString) { + const isCallable = typeof options.callback === 'function'; + // hiding + if (!colorString && isCallable) { options.callback(''); + return; } if ( userActivity && - $inputs[currentFormat].every(el => el.checkValidity()) && - typeof options.callback === 'function' + $inputs[currentFormat].every(el => el.checkValidity()) ) { lastOutputColor = colorString.replace(/\b0\./g, '.'); - options.callback(lastOutputColor); + if (isCallable) { + options.callback(lastOutputColor); + } } }