Issue #1959306 by jessebeach: Drupal.announce does not handle multiple messages at the same time; Improve the utility so that simultaneously calls are read.
parent
ea38078c81
commit
7981d5d07b
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* Adds an HTML element and method to trigger audio UAs to read system messages.
|
||||
*
|
||||
* Use 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.
|
||||
* $('#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) {
|
||||
|
||||
var liveElement;
|
||||
var announcements = [];
|
||||
|
||||
/**
|
||||
* Builds a div element with the aria-live attribute and attaches it
|
||||
* to the DOM.
|
||||
*/
|
||||
Drupal.behaviors.drupalAnnounce = {
|
||||
attach: function (context) {
|
||||
// Create only one aria-live element.
|
||||
if (!liveElement) {
|
||||
liveElement = document.createElement('div');
|
||||
liveElement.id = 'drupal-live-announce';
|
||||
liveElement.className = 'element-invisible';
|
||||
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 () {
|
||||
var text = [];
|
||||
var priority = 'polite';
|
||||
var announcement;
|
||||
|
||||
// Create an array of announcement strings to be joined and appended to the
|
||||
// aria live region.
|
||||
for (var i = 0, il = announcements.length; 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 Drupal.announce() being read.
|
||||
* By wrapping the call to announce in a debounce function, we allow for
|
||||
* time for multiple calls to 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
|
||||
* A string to indicate the priority of the message. Can be either
|
||||
* 'polite' or 'assertive'. Polite is the default.
|
||||
*
|
||||
* @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: text,
|
||||
priority: 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));
|
|
@ -258,66 +258,6 @@ Drupal.t = function (str, args, options) {
|
|||
return str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds an HTML element and method to trigger audio UAs to read system messages.
|
||||
*/
|
||||
var liveElement;
|
||||
|
||||
/**
|
||||
* Builds a div element with the aria-live attribute and attaches it
|
||||
* to the DOM.
|
||||
*/
|
||||
Drupal.behaviors.drupalAnnounce = {
|
||||
attach: function (settings, context) {
|
||||
liveElement = document.createElement('div');
|
||||
liveElement.id = 'drupal-live-announce';
|
||||
liveElement.className = 'element-invisible';
|
||||
liveElement.setAttribute('aria-live', 'polite');
|
||||
liveElement.setAttribute('aria-busy', 'false');
|
||||
document.body.appendChild(liveElement);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Triggers audio UAs to read the supplied text.
|
||||
*
|
||||
* @param {String} text
|
||||
* - A string to be read by the UA.
|
||||
*
|
||||
* @param {String} priority
|
||||
* - A string to indicate the priority of the message. Can be either
|
||||
* 'polite' or 'assertive'. Polite is the default.
|
||||
*
|
||||
* Use 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.
|
||||
* $('#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}
|
||||
* ));
|
||||
* });
|
||||
*
|
||||
* @see http://www.w3.org/WAI/PF/aria-practices/#liveprops
|
||||
*/
|
||||
Drupal.announce = function (text, priority) {
|
||||
if (typeof text === 'string') {
|
||||
// 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 === 'assertive') ? 'assertive' : 'polite');
|
||||
// Print the text to the live region.
|
||||
liveElement.innerHTML = Drupal.checkPlain(text);
|
||||
// The live text area is updated. Allow the AT to announce the text.
|
||||
liveElement.setAttribute('aria-busy', 'false');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the URL to a Drupal page.
|
||||
*/
|
||||
|
|
|
@ -110,6 +110,7 @@ function contextual_library_info() {
|
|||
array('system', 'jquery.once'),
|
||||
array('system', 'backbone'),
|
||||
array('system', 'drupal.tabbingmanager'),
|
||||
array('system', 'drupal.announce'),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -227,6 +227,7 @@ function overlay_library_info() {
|
|||
array('system', 'drupalSettings'),
|
||||
array('system', 'drupal.displace'),
|
||||
array('system', 'drupal.tabbingmanager'),
|
||||
array('system', 'drupal.announce'),
|
||||
array('system', 'jquery.ui.core'),
|
||||
array('system', 'jquery.bbq'),
|
||||
),
|
||||
|
|
|
@ -1252,6 +1252,19 @@ function system_library_info() {
|
|||
),
|
||||
);
|
||||
|
||||
// Drupal's Screen Reader change announcement utility.
|
||||
$libraries['drupal.announce'] = array(
|
||||
'title' => 'Drupal announce',
|
||||
'version' => VERSION,
|
||||
'js' => array(
|
||||
'core/misc/announce.js' => array('group' => JS_LIBRARY),
|
||||
),
|
||||
'dependencies' => array(
|
||||
array('system', 'drupal'),
|
||||
array('system', 'drupal.debounce'),
|
||||
),
|
||||
);
|
||||
|
||||
// Drupal's batch API.
|
||||
$libraries['drupal.batch'] = array(
|
||||
'title' => 'Drupal batch API',
|
||||
|
|
Loading…
Reference in New Issue