Mercurial > hg > cmmr2012-drupal-site
diff core/misc/announce.es6.js @ 0:c75dbcec494b
Initial commit from drush-created site
| author | Chris Cannam |
|---|---|
| date | Thu, 05 Jul 2018 14:24:15 +0000 |
| parents | |
| children | a9cd425dd02b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/misc/announce.es6.js Thu Jul 05 14:24:15 2018 +0000 @@ -0,0 +1,117 @@ +/** + * @file + * Adds an HTML element and method to trigger audio UAs to read system messages. + * + * Use {@link Drupal.announce} to indicate to screen reader users that an + * element on the page has changed state. For instance, if clicking a link + * loads 10 more items into a list, one might announce the change like this. + * + * @example + * $('#search-list') + * .on('itemInsert', function (event, data) { + * // Insert the new items. + * $(data.container.el).append(data.items.el); + * // Announce the change to the page contents. + * Drupal.announce(Drupal.t('@count items added to @container', + * {'@count': data.items.length, '@container': data.container.title} + * )); + * }); + */ + +(function (Drupal, debounce) { + let liveElement; + const announcements = []; + + /** + * Builds a div element with the aria-live attribute and add it to the DOM. + * + * @type {Drupal~behavior} + * + * @prop {Drupal~behaviorAttach} attach + * Attaches the behavior for drupalAnnouce. + */ + Drupal.behaviors.drupalAnnounce = { + attach(context) { + // Create only one aria-live element. + if (!liveElement) { + liveElement = document.createElement('div'); + liveElement.id = 'drupal-live-announce'; + liveElement.className = 'visually-hidden'; + liveElement.setAttribute('aria-live', 'polite'); + liveElement.setAttribute('aria-busy', 'false'); + document.body.appendChild(liveElement); + } + }, + }; + + /** + * Concatenates announcements to a single string; appends to the live region. + */ + function announce() { + const text = []; + let priority = 'polite'; + let announcement; + + // Create an array of announcement strings to be joined and appended to the + // aria live region. + const il = announcements.length; + for (let i = 0; i < il; i++) { + announcement = announcements.pop(); + text.unshift(announcement.text); + // If any of the announcements has a priority of assertive then the group + // of joined announcements will have this priority. + if (announcement.priority === 'assertive') { + priority = 'assertive'; + } + } + + if (text.length) { + // Clear the liveElement so that repeated strings will be read. + liveElement.innerHTML = ''; + // Set the busy state to true until the node changes are complete. + liveElement.setAttribute('aria-busy', 'true'); + // Set the priority to assertive, or default to polite. + liveElement.setAttribute('aria-live', priority); + // Print the text to the live region. Text should be run through + // Drupal.t() before being passed to Drupal.announce(). + liveElement.innerHTML = text.join('\n'); + // The live text area is updated. Allow the AT to announce the text. + liveElement.setAttribute('aria-busy', 'false'); + } + } + + /** + * Triggers audio UAs to read the supplied text. + * + * The aria-live region will only read the text that currently populates its + * text node. Replacing text quickly in rapid calls to announce results in + * only the text from the most recent call to {@link Drupal.announce} being + * read. By wrapping the call to announce in a debounce function, we allow for + * time for multiple calls to {@link Drupal.announce} to queue up their + * messages. These messages are then joined and append to the aria-live region + * as one text node. + * + * @param {string} text + * A string to be read by the UA. + * @param {string} [priority='polite'] + * A string to indicate the priority of the message. Can be either + * 'polite' or 'assertive'. + * + * @return {function} + * The return of the call to debounce. + * + * @see http://www.w3.org/WAI/PF/aria-practices/#liveprops + */ + Drupal.announce = function (text, priority) { + // Save the text and priority into a closure variable. Multiple simultaneous + // announcements will be concatenated and read in sequence. + announcements.push({ + text, + priority, + }); + // Immediately invoke the function that debounce returns. 200 ms is right at + // the cusp where humans notice a pause, so we will wait + // at most this much time before the set of queued announcements is read. + return (debounce(announce, 200)()); + }; +}(Drupal, Drupal.debounce));
