diff --git a/core/misc/announce.js b/core/misc/announce.js index 3037436..4e82876 100644 --- a/core/misc/announce.js +++ b/core/misc/announce.js @@ -26,7 +26,7 @@ * to the DOM. */ Drupal.behaviors.drupalAnnounce = { - attach: function (context) { + attach: function () { // Create only one aria-live element. if (!liveElement) { liveElement = document.createElement('div'); @@ -42,38 +42,43 @@ /** * Concatenates announcements to a single string; appends to the live region. */ - function announce () { + function processAnnounce () { var text = []; - var priority = 'polite'; - var announcement; + var priority, 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 (announcements.length) { + // 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'); + 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'); + } } } + // 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. + var debouncedProcessAnnounce = debounce(processAnnounce, 200); + /** * Triggers audio UAs to read the supplied text. * @@ -98,11 +103,9 @@ // announcements will be concatenated and read in sequence. announcements.push({ text: text, - priority: priority + priority: priority || 'polite' }); - // 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)()); + debouncedProcessAnnounce(announcements); }; + }(Drupal, Drupal.debounce)); diff --git a/core/misc/message.js b/core/misc/message.js new file mode 100644 index 0000000..8c1bbf7 --- /dev/null +++ b/core/misc/message.js @@ -0,0 +1,58 @@ +/** + * + */ +(function (Drupal, debounce) { + + "use strict"; + + var messages = []; + var messagesElement; + + /** + * Builds a div element with the aria-live attribute and attaches it + * to the DOM. + */ + Drupal.behaviors.drupalMessage = { + attach: function () { + if (!messagesElement) { + messagesElement = document.querySelector('[data-drupal-messages]'); + if (!messagesElement) { + throw new Error(Drupal.t("There is no element with a [data-drupal-messages] attribute")); + } + } + } + }; + + + function processMessages () { + var text = []; + var message; + if (messages.length) { + for (var n = 0, nl = messages.length; n < nl; n++) { + message = messages.pop(); + text.unshift(Drupal.theme('message', message.text, message.type)); + } + if (text.length) { + messagesElement.innerHTML += text.join(''); + } + } + } + + // Debounce the function in case Drupal.message() is used in a loop. + var debouncedProcessMessages = debounce(processMessages, 100); + + Drupal.message = function (message, type) { + // Save the text and priority into a closure variable. Multiple simultaneous + // announcements will be concatenated and read in sequence. + messages.push({ + text: message, + type: type || 'status' + }); + debouncedProcessMessages(messages); + }; + + Drupal.theme.message = function (message, type) { + return '
' + message + '
'; + }; + +}(Drupal, Drupal.debounce)); diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 211b79e..036a172 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1189,6 +1189,7 @@ function system_library_info() { 'version' => VERSION, 'js' => array( 'core/misc/announce.js' => array('group' => JS_LIBRARY), + 'core/misc/message.js' => array('group' => JS_LIBRARY), ), 'dependencies' => array( array('system', 'drupal'), diff --git a/core/modules/system/templates/page.html.twig b/core/modules/system/templates/page.html.twig index ff7c010..4cfb432 100644 --- a/core/modules/system/templates/page.html.twig +++ b/core/modules/system/templates/page.html.twig @@ -110,9 +110,7 @@ {{ breadcrumb }} - {% if messages %} -
{{ messages }}
- {% endif %} +
{{ messages }}
diff --git a/core/themes/bartik/templates/page.html.twig b/core/themes/bartik/templates/page.html.twig index 1af8907..3d54927 100644 --- a/core/themes/bartik/templates/page.html.twig +++ b/core/themes/bartik/templates/page.html.twig @@ -132,11 +132,9 @@ {% endif %}
- {% if messages %} -
- {{ messages }} -
- {% endif %} +
+ {{ messages }} +
{% if page.featured %}