diff --git a/packages/accessibility/src/AccessibilityManager.js b/packages/accessibility/src/AccessibilityManager.js index 5fa7857..afd2239 100644 --- a/packages/accessibility/src/AccessibilityManager.js +++ b/packages/accessibility/src/AccessibilityManager.js @@ -227,7 +227,7 @@ const children = displayObject.children; - for (let i = children.length - 1; i >= 0; i--) + for (let i = 0; i < children.length; i++) { this.updateAccessibleObjects(children[i]); } @@ -305,6 +305,17 @@ div.style.width = `${hitArea.width * sx}px`; div.style.height = `${hitArea.height * sy}px`; + + // update button titles and hints if they exist and they've changed + if (div.title !== child.accessibleTitle && child.accessibleTitle !== null) + { + div.title = child.accessibleTitle; + } + if (div.getAttribute('aria-label') !== child.accessibleHint + && child.accessibleHint !== null) + { + div.setAttribute('aria-label', child.accessibleHint); + } } } } @@ -366,21 +377,45 @@ div.style.zIndex = DIV_TOUCH_ZINDEX; div.style.borderStyle = 'none'; + // ARIA attributes ensure that button title and hint updates are announced properly + if (navigator.userAgent.toLowerCase().indexOf('chrome') > -1) + { + // Chrome doesn't need aria-live to work as intended; in fact it just gets more confused. + div.setAttribute('aria-live', 'off'); + } + else + { + div.setAttribute('aria-live', 'polite'); + } + + if (navigator.userAgent.match(/rv:.*Gecko\//)) + { + // FireFox needs this to announce only the new button name + div.setAttribute('aria-relevant', 'additions'); + } + else + { + // required by IE, other browsers don't much care + div.setAttribute('aria-relevant', 'text'); + } + div.addEventListener('click', this._onClick.bind(this)); div.addEventListener('focus', this._onFocus.bind(this)); div.addEventListener('focusout', this._onFocusOut.bind(this)); } - if (displayObject.accessibleTitle) + if (displayObject.accessibleTitle && displayObject.accessibleTitle !== null) { div.title = displayObject.accessibleTitle; } - else if (!displayObject.accessibleTitle && !displayObject.accessibleHint) + else if (!displayObject.accessibleHint + || displayObject.accessibleHint === null) { - div.title = `displayObject ${this.tabIndex}`; + div.title = `displayObject ${displayObject.tabIndex}`; } - if (displayObject.accessibleHint) + if (displayObject.accessibleHint + && displayObject.accessibleHint !== null) { div.setAttribute('aria-label', displayObject.accessibleHint); } @@ -417,6 +452,10 @@ */ _onFocus(e) { + if (!e.target.getAttribute('aria-live', 'off')) + { + e.target.setAttribute('aria-live', 'assertive'); + } const interactionManager = this.renderer.plugins.interaction; interactionManager.dispatchEvent(e.target.displayObject, 'mouseover', interactionManager.eventData); @@ -430,6 +469,10 @@ */ _onFocusOut(e) { + if (!e.target.getAttribute('aria-live', 'off')) + { + e.target.setAttribute('aria-live', 'polite'); + } const interactionManager = this.renderer.plugins.interaction; interactionManager.dispatchEvent(e.target.displayObject, 'mouseout', interactionManager.eventData); @@ -455,9 +498,15 @@ * Is called when the mouse moves across the renderer element * * @private + * @param {MouseEvent} e - The mouse event. */ - _onMouseMove() + _onMouseMove(e) { + if (e.movementX === 0 && e.movementY === 0) + { + return; + } + this.deactivate(); }