Skip to content

Commit 933ce5d

Browse files
authoredJan 5, 2022
Autocomplete: Rewrite with a delay instead of appending the live region
This fixes the issue caused by https://bugs.jqueryui.com/ticket/9357. We now empty the live region instead of appending to it, and we do so after a brief timeout so the live region isn't updated on every mousemove event or when quickly traversing through options. Fixes gh-2002 Closes gh-2031
1 parent e90096e commit 933ce5d

File tree

2 files changed

+55
-38
lines changed

2 files changed

+55
-38
lines changed
 

‎tests/unit/autocomplete/core.js

+46-34
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ QUnit.test( "simultaneous searches (#9334)", function( assert ) {
293293
} );
294294

295295
QUnit.test( "ARIA", function( assert ) {
296+
var ready = assert.async();
296297
assert.expect( 13 );
297298
var element = $( "#autocomplete" ).autocomplete( {
298299
source: [ "java", "javascript" ]
@@ -308,43 +309,51 @@ QUnit.test( "ARIA", function( assert ) {
308309
"Live region's role attribute must be status" );
309310

310311
element.autocomplete( "search", "j" );
311-
assert.equal( liveRegion.children().first().text(),
312-
"2 results are available, use up and down arrow keys to navigate.",
313-
"Live region for multiple values" );
312+
setTimeout( function() {
313+
assert.equal( liveRegion.children().first().text(),
314+
"2 results are available, use up and down arrow keys to navigate.",
315+
"Live region for multiple values" );
314316

315-
element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
316-
assert.equal( liveRegion.children().filter( ":visible" ).text(), "java",
317-
"Live region changed on keydown to announce the highlighted value" );
317+
element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
318+
setTimeout( function() {
319+
assert.equal( liveRegion.children().filter( ":visible" ).text(), "java",
320+
"Live region changed on keydown to announce the highlighted value" );
318321

319-
element.one( "autocompletefocus", function( event ) {
320-
event.preventDefault();
321-
} );
322-
element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
323-
assert.equal( liveRegion.children().filter( ":visible" ).text(), "javascript",
324-
"Live region updated when default focus is prevented" );
325-
326-
element.autocomplete( "search", "javas" );
327-
assert.equal( liveRegion.children().filter( ":visible" ).text(),
328-
"1 result is available, use up and down arrow keys to navigate.",
329-
"Live region for one value" );
330-
331-
element.autocomplete( "search", "z" );
332-
assert.equal( liveRegion.children().filter( ":visible" ).text(), "No search results.",
333-
"Live region for no values" );
334-
335-
assert.equal( liveRegion.children().length, 5,
336-
"Should be five children in the live region after the above" );
337-
assert.equal( liveRegion.children().filter( ":visible" ).length, 1,
338-
"Only one should be still visible" );
339-
assert.ok( liveRegion.children().filter( ":visible" )[ 0 ] === liveRegion.children().last()[ 0 ],
340-
"The last one should be the visible one" );
341-
342-
element.autocomplete( "destroy" );
343-
assert.equal( liveRegion.parent().length, 0,
344-
"The liveRegion should be detached after destroy" );
322+
element.one( "autocompletefocus", function( event ) {
323+
event.preventDefault();
324+
} );
325+
element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
326+
setTimeout( function() {
327+
assert.equal( liveRegion.children().filter( ":visible" ).text(), "javascript",
328+
"Live region updated when default focus is prevented" );
329+
element.autocomplete( "search", "javas" );
330+
setTimeout( function() {
331+
assert.equal( liveRegion.children().filter( ":visible" ).text(),
332+
"1 result is available, use up and down arrow keys to navigate.",
333+
"Live region for one value" );
334+
element.autocomplete( "search", "z" );
335+
setTimeout( function() {
336+
assert.equal( liveRegion.children().filter( ":visible" ).text(), "No search results.",
337+
"Live region for no values" );
338+
assert.equal( liveRegion.children().length, 1,
339+
"Should be one child in the live region after the above" );
340+
assert.equal( liveRegion.children().filter( ":visible" ).length, 1,
341+
"Only one should be still visible" );
342+
assert.ok( liveRegion.children().filter( ":visible" )[ 0 ] === liveRegion.children().last()[ 0 ],
343+
"The last one should be the visible one" );
344+
element.autocomplete( "destroy" );
345+
assert.equal( liveRegion.parent().length, 0,
346+
"The liveRegion should be detached after destroy" );
347+
ready();
348+
}, 110 );
349+
}, 110 );
350+
}, 110 );
351+
}, 110 );
352+
}, 110 );
345353
} );
346354

347355
QUnit.test( "ARIA, aria-label announcement", function( assert ) {
356+
var ready = assert.async();
348357
assert.expect( 1 );
349358
$.widget( "custom.catcomplete", $.ui.autocomplete, {
350359
_renderMenu: function( ul, items ) {
@@ -361,8 +370,11 @@ QUnit.test( "ARIA, aria-label announcement", function( assert ) {
361370
liveRegion = element.catcomplete( "instance" ).liveRegion;
362371
element.catcomplete( "search", "a" );
363372
element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } );
364-
assert.equal( liveRegion.children().filter( ":visible" ).text(), "People : anders andersson",
365-
"Live region changed on keydown to announce the highlighted value's aria-label attribute" );
373+
setTimeout( function() {
374+
assert.equal( liveRegion.children().filter( ":visible" ).text(), "People : anders andersson",
375+
"Live region changed on keydown to announce the highlighted value's aria-label attribute" );
376+
ready();
377+
}, 110 );
366378
} );
367379

368380
QUnit.test( "ARIA, init on detached input", function( assert ) {

‎ui/widgets/autocomplete.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ $.widget( "ui.autocomplete", {
6666

6767
requestIndex: 0,
6868
pending: 0,
69+
liveRegionTimer: null,
6970

7071
_create: function() {
7172

@@ -267,8 +268,10 @@ $.widget( "ui.autocomplete", {
267268
// Announce the value in the liveRegion
268269
label = ui.item.attr( "aria-label" ) || item.value;
269270
if ( label && String.prototype.trim.call( label ).length ) {
270-
this.liveRegion.children().hide();
271-
$( "<div>" ).text( label ).appendTo( this.liveRegion );
271+
clearTimeout( this.liveRegionTimer );
272+
this.liveRegionTimer = this._delay( function() {
273+
this.liveRegion.html( $( "<div>" ).text( label ) );
274+
}, 100 );
272275
}
273276
},
274277
menuselect: function( event, ui ) {
@@ -663,8 +666,10 @@ $.widget( "ui.autocomplete", $.ui.autocomplete, {
663666
} else {
664667
message = this.options.messages.noResults;
665668
}
666-
this.liveRegion.children().hide();
667-
$( "<div>" ).text( message ).appendTo( this.liveRegion );
669+
clearTimeout( this.liveRegionTimer );
670+
this.liveRegionTimer = this._delay( function() {
671+
this.liveRegion.html( $( "<div>" ).text( message ) );
672+
}, 100 );
668673
}
669674
} );
670675

0 commit comments

Comments
 (0)
Please sign in to comment.