From dbd592b3a2e4249ce5604dcefac6a1c7a7e30796 Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Sat, 16 Mar 2019 19:47:16 +0100 Subject: [PATCH] Improved a11y of interactive drop-downs See #2713 --- wcfsetup/install/files/js/WCF.User.js | 53 +++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/wcfsetup/install/files/js/WCF.User.js b/wcfsetup/install/files/js/WCF.User.js index fa3b6ea187..052d4f961c 100644 --- a/wcfsetup/install/files/js/WCF.User.js +++ b/wcfsetup/install/files/js/WCF.User.js @@ -164,6 +164,21 @@ if (COMPILER_TARGET_DEFAULT) { */ _triggerElement: null, + /** + * @var Element + */ + _button: null, + + /** + * @var Function + */ + _callbackFocus: null, + + /** + * @var boolean + */ + _wasInsideDropdown: false, + /** * Initializes the WCF.User.Panel.Abstract class. * @@ -177,6 +192,7 @@ if (COMPILER_TARGET_DEFAULT) { this._identifier = identifier; this._triggerElement = triggerElement; this._options = options; + this._callbackFocus = null; this._proxy = new WCF.Action.Proxy({ showLoadingOverlay: false, @@ -184,6 +200,13 @@ if (COMPILER_TARGET_DEFAULT) { }); this._triggerElement.click($.proxy(this.toggle, this)); + this._button = elBySel('a', this._triggerElement[0]); + if (this._button) { + elAttr(this._button, 'role', 'button'); + elAttr(this._button, 'tabindex', '0'); + elAttr(this._button, 'aria-haspopup', true); + elAttr(this._button, 'aria-expanded', false); + } if (this._options.showAllLink) { this._triggerElement.dblclick($.proxy(this._dblClick, this)); @@ -230,6 +253,16 @@ if (COMPILER_TARGET_DEFAULT) { this._loadData = false; this._load(); } + + elAttr(this._button, 'aria-expanded', true); + if (this._callbackFocus === null) { + this._callbackFocus = this._maintainFocus.bind(this); + } + document.body.addEventListener('focus', this._callbackFocus, { capture: true }); + } + else { + elAttr(this._button, 'aria-expanded', false); + document.body.removeEventListener('focus', this._callbackFocus); } return false; @@ -416,6 +449,26 @@ if (COMPILER_TARGET_DEFAULT) { this._dropdown.resetItems(); this._loadData = true; } + }, + + /** + * @param {Event} event + */ + _maintainFocus: function(event) { + var dropdown = this._dropdown.getContainer()[0]; + + if (!dropdown.contains(event.target)) { + if (this._wasInsideDropdown) { + this._button.focus(); + this._wasInsideDropdown = false; + } + else { + elBySel('a', dropdown).focus(); + } + } + else { + this._wasInsideDropdown = true; + } } }); -- 2.20.1