From 4628e9e5a2bf3eea93e922d38f6c4fbccf551609 Mon Sep 17 00:00:00 2001 From: Rachel Vasquez Date: Thu, 11 Jun 2026 08:08:18 -0400 Subject: [PATCH 1/2] experimenting with live region fix, still testing w/ NVDA --- js/pmpro-liquid-autocomplete.js | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/js/pmpro-liquid-autocomplete.js b/js/pmpro-liquid-autocomplete.js index a7826d95a..158c55e4b 100644 --- a/js/pmpro-liquid-autocomplete.js +++ b/js/pmpro-liquid-autocomplete.js @@ -77,6 +77,31 @@ return menu; } + function ensureAnnouncer() { + let announcer = document.getElementById( + 'pmpro-liquid-autocomplete-announcer' + ); + + if ( ! announcer ) { + announcer = document.createElement( 'div' ); + announcer.id = 'pmpro-liquid-autocomplete-announcer'; + announcer.setAttribute( 'aria-live', 'assertive' ); + announcer.setAttribute( 'aria-atomic', 'true' ); + announcer.className = 'screen-reader-text'; + document.body.appendChild( announcer ); + } + + return announcer; + } + + function announce( text ) { + const announcer = ensureAnnouncer(); + announcer.textContent = ''; + requestAnimationFrame( function () { + announcer.textContent = text; + } ); + } + function closest( node, selector ) { while ( node && node !== document ) { if ( node.matches && node.matches( selector ) ) { @@ -96,6 +121,14 @@ menu.removeAttribute( 'aria-activedescendant' ); } + const announcer = document.getElementById( + 'pmpro-liquid-autocomplete-announcer' + ); + + if ( announcer ) { + announcer.textContent = ''; + } + items = []; activeIndex = -1; activeContext = null; @@ -196,6 +229,18 @@ 'aria-activedescendant', 'pmpro-liquid-autocomplete-option-' + activeIndex ); + + const item = items[ activeIndex ]; + const selectableItems = items.filter( function ( i ) { + return i.type !== 'separator'; + } ); + const position = selectableItems.indexOf( item ) + 1; + const total = selectableItems.length; + announce( + item.label + + ( item.description ? ', ' + item.description : '' ) + + ', ' + position + ' of ' + total + ); } function moveActive( direction ) { @@ -553,6 +598,10 @@ event.preventDefault(); event.stopPropagation(); closeMenu(); + announce( + strings.autocompleteClosed || + 'Autocomplete list closed' + ); } else if ( event.keyCode === 38 ) { event.preventDefault(); event.stopPropagation(); From cb50fc428f535d13fa999a517d9d733b6d56fa9f Mon Sep 17 00:00:00 2001 From: Rachel Vasquez Date: Thu, 11 Jun 2026 14:37:12 -0400 Subject: [PATCH 2/2] Tweaks after testing announcements when menu opens and closes w/ esc key, still testing --- js/pmpro-liquid-autocomplete.js | 69 +++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/js/pmpro-liquid-autocomplete.js b/js/pmpro-liquid-autocomplete.js index 158c55e4b..3d8c20982 100644 --- a/js/pmpro-liquid-autocomplete.js +++ b/js/pmpro-liquid-autocomplete.js @@ -77,15 +77,19 @@ return menu; } - function ensureAnnouncer() { - let announcer = document.getElementById( - 'pmpro-liquid-autocomplete-announcer' - ); + function ensureAnnouncer( politeness ) { + const id = 'polite' === politeness + ? 'pmpro-liquid-autocomplete-announcer-polite' + : 'pmpro-liquid-autocomplete-announcer'; + let announcer = document.getElementById( id ); if ( ! announcer ) { announcer = document.createElement( 'div' ); - announcer.id = 'pmpro-liquid-autocomplete-announcer'; - announcer.setAttribute( 'aria-live', 'assertive' ); + announcer.id = id; + announcer.setAttribute( + 'aria-live', + 'polite' === politeness ? 'polite' : 'assertive' + ); announcer.setAttribute( 'aria-atomic', 'true' ); announcer.className = 'screen-reader-text'; document.body.appendChild( announcer ); @@ -94,8 +98,8 @@ return announcer; } - function announce( text ) { - const announcer = ensureAnnouncer(); + function announce( text, politeness ) { + const announcer = ensureAnnouncer( politeness ); announcer.textContent = ''; requestAnimationFrame( function () { announcer.textContent = text; @@ -121,13 +125,15 @@ menu.removeAttribute( 'aria-activedescendant' ); } - const announcer = document.getElementById( - 'pmpro-liquid-autocomplete-announcer' - ); - - if ( announcer ) { - announcer.textContent = ''; - } + [ + 'pmpro-liquid-autocomplete-announcer', + 'pmpro-liquid-autocomplete-announcer-polite', + ].forEach( function ( id ) { + const announcer = document.getElementById( id ); + if ( announcer ) { + announcer.textContent = ''; + } + } ); items = []; activeIndex = -1; @@ -190,12 +196,13 @@ container.appendChild( itemNode ); } ); + const isOpening = container.hidden; container.hidden = false; positionMenu( context ); - setActive( firstSelectable ); + setActive( firstSelectable, isOpening ); } - function setActive( index ) { + function setActive( index, isOpening ) { if ( ! items[ index ] || items[ index ].type === 'separator' ) { return; } @@ -236,10 +243,17 @@ } ); const position = selectableItems.indexOf( item ) + 1; const total = selectableItems.length; - announce( + const itemText = item.label + ( item.description ? ', ' + item.description : '' ) + - ', ' + position + ' of ' + total + ', ' + position + ' of ' + total; + announce( + isOpening + ? ( strings.autocompleteOpened || + 'Autocomplete list opened' ) + + ', ' + itemText + : itemText, + isOpening ? 'polite' : 'assertive' ); } @@ -571,11 +585,18 @@ function shouldIgnoreKeyup( event ) { return ( - event.keyCode === 13 || - event.keyCode === 27 || - event.keyCode === 38 || - event.keyCode === 40 || - event.keyCode === 9 + event.keyCode === 9 || // Tab + event.keyCode === 13 || // Enter + event.keyCode === 16 || // Shift + event.keyCode === 17 || // Ctrl + event.keyCode === 18 || // Alt + event.keyCode === 20 || // Caps Lock + event.keyCode === 27 || // Escape + event.keyCode === 38 || // Up arrow + event.keyCode === 40 || // Down arrow + event.keyCode === 91 || // Meta left + event.keyCode === 92 || // Meta right + event.keyCode === 93 // Context menu / Meta ); }