@@ -37,7 +37,7 @@ export const CDK_DESCRIBEDBY_HOST_ATTRIBUTE = 'cdk-describedby-host';
37
37
let nextId = 0 ;
38
38
39
39
/** Global map of all registered message elements that have been placed into the document. */
40
- const messageRegistry = new Map < string | HTMLElement , RegisteredMessage > ( ) ;
40
+ const messageRegistry = new Map < string | Element , RegisteredMessage > ( ) ;
41
41
42
42
/** Container for all registered messages. */
43
43
let messagesContainer : HTMLElement | null = null ;
@@ -65,40 +65,56 @@ export class AriaDescriber implements OnDestroy {
65
65
* the message. If the same message has already been registered, then it will reuse the created
66
66
* message element.
67
67
*/
68
- describe ( hostElement : Element , message : string | HTMLElement ) {
68
+ describe ( hostElement : Element , message : string , role ?: string ) : void ;
69
+
70
+ /**
71
+ * Adds to the host element an aria-describedby reference to an already-existing messsage element.
72
+ */
73
+ describe ( hostElement : Element , message : HTMLElement ) : void ;
74
+
75
+ describe ( hostElement : Element , message : string | HTMLElement , role ?: string ) : void {
69
76
if ( ! this . _canBeDescribed ( hostElement , message ) ) {
70
77
return ;
71
78
}
72
79
80
+ const key = getKey ( message , role ) ;
81
+
73
82
if ( typeof message !== 'string' ) {
74
83
// We need to ensure that the element has an ID.
75
- this . _setMessageId ( message ) ;
76
- messageRegistry . set ( message , { messageElement : message , referenceCount : 0 } ) ;
77
- } else if ( ! messageRegistry . has ( message ) ) {
78
- this . _createMessageElement ( message ) ;
84
+ setMessageId ( message ) ;
85
+ messageRegistry . set ( key , { messageElement : message , referenceCount : 0 } ) ;
86
+ } else if ( ! messageRegistry . has ( key ) ) {
87
+ this . _createMessageElement ( message , role ) ;
79
88
}
80
89
81
- if ( ! this . _isElementDescribedByMessage ( hostElement , message ) ) {
82
- this . _addMessageReference ( hostElement , message ) ;
90
+ if ( ! this . _isElementDescribedByMessage ( hostElement , key ) ) {
91
+ this . _addMessageReference ( hostElement , key ) ;
83
92
}
84
93
}
85
94
95
+ /** Removes the host element's aria-describedby reference to the message. */
96
+ removeDescription ( hostElement : Element , message : string , role ?: string ) : void ;
97
+
86
98
/** Removes the host element's aria-describedby reference to the message element. */
87
- removeDescription ( hostElement : Element , message : string | HTMLElement ) {
99
+ removeDescription ( hostElement : Element , message : HTMLElement ) : void ;
100
+
101
+ removeDescription ( hostElement : Element , message : string | HTMLElement , role ?: string ) : void {
88
102
if ( ! message || ! this . _isElementNode ( hostElement ) ) {
89
103
return ;
90
104
}
91
105
92
- if ( this . _isElementDescribedByMessage ( hostElement , message ) ) {
93
- this . _removeMessageReference ( hostElement , message ) ;
106
+ const key = getKey ( message , role ) ;
107
+
108
+ if ( this . _isElementDescribedByMessage ( hostElement , key ) ) {
109
+ this . _removeMessageReference ( hostElement , key ) ;
94
110
}
95
111
96
112
// If the message is a string, it means that it's one that we created for the
97
113
// consumer so we can remove it safely, otherwise we should leave it in place.
98
114
if ( typeof message === 'string' ) {
99
- const registeredMessage = messageRegistry . get ( message ) ;
115
+ const registeredMessage = messageRegistry . get ( key ) ;
100
116
if ( registeredMessage && registeredMessage . referenceCount === 0 ) {
101
- this . _deleteMessageElement ( message ) ;
117
+ this . _deleteMessageElement ( key ) ;
102
118
}
103
119
}
104
120
@@ -128,32 +144,28 @@ export class AriaDescriber implements OnDestroy {
128
144
* Creates a new element in the visually hidden message container element with the message
129
145
* as its content and adds it to the message registry.
130
146
*/
131
- private _createMessageElement ( message : string ) {
147
+ private _createMessageElement ( message : string , role ?: string ) {
132
148
const messageElement = this . _document . createElement ( 'div' ) ;
133
- this . _setMessageId ( messageElement ) ;
149
+ setMessageId ( messageElement ) ;
134
150
messageElement . textContent = message ;
135
151
152
+ if ( role ) {
153
+ messageElement . setAttribute ( 'role' , role ) ;
154
+ }
155
+
136
156
this . _createMessagesContainer ( ) ;
137
157
messagesContainer ! . appendChild ( messageElement ) ;
138
-
139
- messageRegistry . set ( message , { messageElement, referenceCount : 0 } ) ;
140
- }
141
-
142
- /** Assigns a unique ID to an element, if it doesn't have one already. */
143
- private _setMessageId ( element : HTMLElement ) {
144
- if ( ! element . id ) {
145
- element . id = `${ CDK_DESCRIBEDBY_ID_PREFIX } -${ nextId ++ } ` ;
146
- }
158
+ messageRegistry . set ( getKey ( message , role ) , { messageElement, referenceCount : 0 } ) ;
147
159
}
148
160
149
161
/** Deletes the message element from the global messages container. */
150
- private _deleteMessageElement ( message : string ) {
151
- const registeredMessage = messageRegistry . get ( message ) ;
162
+ private _deleteMessageElement ( key : string | Element ) {
163
+ const registeredMessage = messageRegistry . get ( key ) ;
152
164
const messageElement = registeredMessage && registeredMessage . messageElement ;
153
165
if ( messagesContainer && messageElement ) {
154
166
messagesContainer . removeChild ( messageElement ) ;
155
167
}
156
- messageRegistry . delete ( message ) ;
168
+ messageRegistry . delete ( key ) ;
157
169
}
158
170
159
171
/** Creates the global container for all aria-describedby messages. */
@@ -204,33 +216,32 @@ export class AriaDescriber implements OnDestroy {
204
216
* Adds a message reference to the element using aria-describedby and increments the registered
205
217
* message's reference count.
206
218
*/
207
- private _addMessageReference ( element : Element , message : string | HTMLElement ) {
208
- const registeredMessage = messageRegistry . get ( message ) ! ;
219
+ private _addMessageReference ( element : Element , key : string | Element ) {
220
+ const registeredMessage = messageRegistry . get ( key ) ! ;
209
221
210
222
// Add the aria-describedby reference and set the
211
223
// describedby_host attribute to mark the element.
212
224
addAriaReferencedId ( element , 'aria-describedby' , registeredMessage . messageElement . id ) ;
213
225
element . setAttribute ( CDK_DESCRIBEDBY_HOST_ATTRIBUTE , '' ) ;
214
-
215
226
registeredMessage . referenceCount ++ ;
216
227
}
217
228
218
229
/**
219
230
* Removes a message reference from the element using aria-describedby
220
231
* and decrements the registered message's reference count.
221
232
*/
222
- private _removeMessageReference ( element : Element , message : string | HTMLElement ) {
223
- const registeredMessage = messageRegistry . get ( message ) ! ;
233
+ private _removeMessageReference ( element : Element , key : string | Element ) {
234
+ const registeredMessage = messageRegistry . get ( key ) ! ;
224
235
registeredMessage . referenceCount -- ;
225
236
226
237
removeAriaReferencedId ( element , 'aria-describedby' , registeredMessage . messageElement . id ) ;
227
238
element . removeAttribute ( CDK_DESCRIBEDBY_HOST_ATTRIBUTE ) ;
228
239
}
229
240
230
241
/** Returns true if the element has been described by the provided message ID. */
231
- private _isElementDescribedByMessage ( element : Element , message : string | HTMLElement ) : boolean {
242
+ private _isElementDescribedByMessage ( element : Element , key : string | Element ) : boolean {
232
243
const referenceIds = getAriaReferenceIds ( element , 'aria-describedby' ) ;
233
- const registeredMessage = messageRegistry . get ( message ) ;
244
+ const registeredMessage = messageRegistry . get ( key ) ;
234
245
const messageId = registeredMessage && registeredMessage . messageElement . id ;
235
246
236
247
return ! ! messageId && referenceIds . indexOf ( messageId ) != - 1 ;
@@ -262,3 +273,15 @@ export class AriaDescriber implements OnDestroy {
262
273
return element . nodeType === this . _document . ELEMENT_NODE ;
263
274
}
264
275
}
276
+
277
+ /** Gets a key that can be used to look messages up in the registry. */
278
+ function getKey ( message : string | Element , role ?: string ) : string | Element {
279
+ return typeof message === 'string' ? `${ role || '' } /${ message } ` : message ;
280
+ }
281
+
282
+ /** Assigns a unique ID to an element, if it doesn't have one already. */
283
+ function setMessageId ( element : HTMLElement ) {
284
+ if ( ! element . id ) {
285
+ element . id = `${ CDK_DESCRIBEDBY_ID_PREFIX } -${ nextId ++ } ` ;
286
+ }
287
+ }
0 commit comments