From: Ben Mullins Date: Wed, 5 Jan 2022 10:35:34 +0000 (-0500) Subject: Autocomplete: Rewrite with a delay instead of appending the live region X-Git-Tag: 1.13.1~4 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=933ce5d779135ad04734f14c50b38f9a8d8564f5;p=jquery-ui.git 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 --- diff --git a/tests/unit/autocomplete/core.js b/tests/unit/autocomplete/core.js index d78db3bb9..a2a36cb4c 100644 --- a/tests/unit/autocomplete/core.js +++ b/tests/unit/autocomplete/core.js @@ -293,6 +293,7 @@ QUnit.test( "simultaneous searches (#9334)", function( assert ) { } ); QUnit.test( "ARIA", function( assert ) { + var ready = assert.async(); assert.expect( 13 ); var element = $( "#autocomplete" ).autocomplete( { source: [ "java", "javascript" ] @@ -308,43 +309,51 @@ QUnit.test( "ARIA", function( assert ) { "Live region's role attribute must be status" ); element.autocomplete( "search", "j" ); - assert.equal( liveRegion.children().first().text(), - "2 results are available, use up and down arrow keys to navigate.", - "Live region for multiple values" ); + setTimeout( function() { + assert.equal( liveRegion.children().first().text(), + "2 results are available, use up and down arrow keys to navigate.", + "Live region for multiple values" ); - element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); - assert.equal( liveRegion.children().filter( ":visible" ).text(), "java", - "Live region changed on keydown to announce the highlighted value" ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout( function() { + assert.equal( liveRegion.children().filter( ":visible" ).text(), "java", + "Live region changed on keydown to announce the highlighted value" ); - element.one( "autocompletefocus", function( event ) { - event.preventDefault(); - } ); - element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); - assert.equal( liveRegion.children().filter( ":visible" ).text(), "javascript", - "Live region updated when default focus is prevented" ); - - element.autocomplete( "search", "javas" ); - assert.equal( liveRegion.children().filter( ":visible" ).text(), - "1 result is available, use up and down arrow keys to navigate.", - "Live region for one value" ); - - element.autocomplete( "search", "z" ); - assert.equal( liveRegion.children().filter( ":visible" ).text(), "No search results.", - "Live region for no values" ); - - assert.equal( liveRegion.children().length, 5, - "Should be five children in the live region after the above" ); - assert.equal( liveRegion.children().filter( ":visible" ).length, 1, - "Only one should be still visible" ); - assert.ok( liveRegion.children().filter( ":visible" )[ 0 ] === liveRegion.children().last()[ 0 ], - "The last one should be the visible one" ); - - element.autocomplete( "destroy" ); - assert.equal( liveRegion.parent().length, 0, - "The liveRegion should be detached after destroy" ); + element.one( "autocompletefocus", function( event ) { + event.preventDefault(); + } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout( function() { + assert.equal( liveRegion.children().filter( ":visible" ).text(), "javascript", + "Live region updated when default focus is prevented" ); + element.autocomplete( "search", "javas" ); + setTimeout( function() { + assert.equal( liveRegion.children().filter( ":visible" ).text(), + "1 result is available, use up and down arrow keys to navigate.", + "Live region for one value" ); + element.autocomplete( "search", "z" ); + setTimeout( function() { + assert.equal( liveRegion.children().filter( ":visible" ).text(), "No search results.", + "Live region for no values" ); + assert.equal( liveRegion.children().length, 1, + "Should be one child in the live region after the above" ); + assert.equal( liveRegion.children().filter( ":visible" ).length, 1, + "Only one should be still visible" ); + assert.ok( liveRegion.children().filter( ":visible" )[ 0 ] === liveRegion.children().last()[ 0 ], + "The last one should be the visible one" ); + element.autocomplete( "destroy" ); + assert.equal( liveRegion.parent().length, 0, + "The liveRegion should be detached after destroy" ); + ready(); + }, 110 ); + }, 110 ); + }, 110 ); + }, 110 ); + }, 110 ); } ); QUnit.test( "ARIA, aria-label announcement", function( assert ) { + var ready = assert.async(); assert.expect( 1 ); $.widget( "custom.catcomplete", $.ui.autocomplete, { _renderMenu: function( ul, items ) { @@ -361,8 +370,11 @@ QUnit.test( "ARIA, aria-label announcement", function( assert ) { liveRegion = element.catcomplete( "instance" ).liveRegion; element.catcomplete( "search", "a" ); element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); - assert.equal( liveRegion.children().filter( ":visible" ).text(), "People : anders andersson", - "Live region changed on keydown to announce the highlighted value's aria-label attribute" ); + setTimeout( function() { + assert.equal( liveRegion.children().filter( ":visible" ).text(), "People : anders andersson", + "Live region changed on keydown to announce the highlighted value's aria-label attribute" ); + ready(); + }, 110 ); } ); QUnit.test( "ARIA, init on detached input", function( assert ) { diff --git a/ui/widgets/autocomplete.js b/ui/widgets/autocomplete.js index 4166029b2..a7b6f52bd 100644 --- a/ui/widgets/autocomplete.js +++ b/ui/widgets/autocomplete.js @@ -66,6 +66,7 @@ $.widget( "ui.autocomplete", { requestIndex: 0, pending: 0, + liveRegionTimer: null, _create: function() { @@ -267,8 +268,10 @@ $.widget( "ui.autocomplete", { // Announce the value in the liveRegion label = ui.item.attr( "aria-label" ) || item.value; if ( label && String.prototype.trim.call( label ).length ) { - this.liveRegion.children().hide(); - $( "
" ).text( label ).appendTo( this.liveRegion ); + clearTimeout( this.liveRegionTimer ); + this.liveRegionTimer = this._delay( function() { + this.liveRegion.html( $( "
" ).text( label ) ); + }, 100 ); } }, menuselect: function( event, ui ) { @@ -663,8 +666,10 @@ $.widget( "ui.autocomplete", $.ui.autocomplete, { } else { message = this.options.messages.noResults; } - this.liveRegion.children().hide(); - $( "
" ).text( message ).appendTo( this.liveRegion ); + clearTimeout( this.liveRegionTimer ); + this.liveRegionTimer = this._delay( function() { + this.liveRegion.html( $( "
" ).text( message ) ); + }, 100 ); } } );