2015-08-17 17:00:26 -07:00
/ * *
* @ 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 ) {
2015-10-21 21:44:50 -07:00
'use strict' ;
2015-08-17 17:00:26 -07:00
var liveElement ;
var announcements = [ ] ;
/ * *
* Builds a div element with the aria - live attribute and add it to the DOM .
*
* @ type { Drupal ~ behavior }
* /
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 = '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 ( ) {
var text = [ ] ;
var priority = 'polite' ;
var announcement ;
// Create an array of announcement strings to be joined and appended to the
// aria live region.
var il = announcements . length ;
for ( var 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 }
*
* @ 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 ) ) ;