diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js index cfc49d7..99ca617 100644 --- a/src/accessibility/AccessibilityManager.js +++ b/src/accessibility/AccessibilityManager.js @@ -226,7 +226,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]); } @@ -304,6 +304,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); + } } } } @@ -365,21 +376,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); } @@ -416,6 +451,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); @@ -429,6 +468,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); @@ -454,9 +497,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(); }