announce.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. /**
  2. * @license
  3. * Copyright 2020 Google Inc.
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. * THE SOFTWARE.
  22. */
  23. /**
  24. * Priorities for the announce function.
  25. */
  26. export var AnnouncerPriority;
  27. (function (AnnouncerPriority) {
  28. AnnouncerPriority["POLITE"] = "polite";
  29. AnnouncerPriority["ASSERTIVE"] = "assertive";
  30. })(AnnouncerPriority || (AnnouncerPriority = {}));
  31. /**
  32. * Data attribute added to live region element.
  33. */
  34. export var DATA_MDC_DOM_ANNOUNCE = 'data-mdc-dom-announce';
  35. /**
  36. * Announces the given message with optional priority, defaulting to "polite"
  37. */
  38. export function announce(message, options) {
  39. Announcer.getInstance().say(message, options);
  40. }
  41. var Announcer = /** @class */ (function () {
  42. // Constructor made private to ensure only the singleton is used
  43. function Announcer() {
  44. this.liveRegions = new Map();
  45. }
  46. Announcer.getInstance = function () {
  47. if (!Announcer.instance) {
  48. Announcer.instance = new Announcer();
  49. }
  50. return Announcer.instance;
  51. };
  52. Announcer.prototype.say = function (message, options) {
  53. var _a, _b;
  54. var priority = (_a = options === null || options === void 0 ? void 0 : options.priority) !== null && _a !== void 0 ? _a : AnnouncerPriority.POLITE;
  55. var ownerDocument = (_b = options === null || options === void 0 ? void 0 : options.ownerDocument) !== null && _b !== void 0 ? _b : document;
  56. var liveRegion = this.getLiveRegion(priority, ownerDocument);
  57. // Reset the region to pick up the message, even if the message is the
  58. // exact same as before.
  59. liveRegion.textContent = '';
  60. // Timeout is necessary for screen readers like NVDA and VoiceOver.
  61. setTimeout(function () {
  62. liveRegion.textContent = message;
  63. ownerDocument.addEventListener('click', clearLiveRegion);
  64. }, 1);
  65. function clearLiveRegion() {
  66. liveRegion.textContent = '';
  67. ownerDocument.removeEventListener('click', clearLiveRegion);
  68. }
  69. };
  70. Announcer.prototype.getLiveRegion = function (priority, ownerDocument) {
  71. var documentLiveRegions = this.liveRegions.get(ownerDocument);
  72. if (!documentLiveRegions) {
  73. documentLiveRegions = new Map();
  74. this.liveRegions.set(ownerDocument, documentLiveRegions);
  75. }
  76. var existingLiveRegion = documentLiveRegions.get(priority);
  77. if (existingLiveRegion && ownerDocument.body.contains(existingLiveRegion)) {
  78. return existingLiveRegion;
  79. }
  80. var liveRegion = this.createLiveRegion(priority, ownerDocument);
  81. documentLiveRegions.set(priority, liveRegion);
  82. return liveRegion;
  83. };
  84. Announcer.prototype.createLiveRegion = function (priority, ownerDocument) {
  85. var el = ownerDocument.createElement('div');
  86. el.style.position = 'absolute';
  87. el.style.top = '-9999px';
  88. el.style.left = '-9999px';
  89. el.style.height = '1px';
  90. el.style.overflow = 'hidden';
  91. el.setAttribute('aria-atomic', 'true');
  92. el.setAttribute('aria-live', priority);
  93. el.setAttribute(DATA_MDC_DOM_ANNOUNCE, 'true');
  94. ownerDocument.body.appendChild(el);
  95. return el;
  96. };
  97. return Announcer;
  98. }());
  99. //# sourceMappingURL=announce.js.map