Skip to content

Commit 548fbf5

Browse files
committed
Autocomplete: Close the menu on any outside interactions
This ensures that the menu will close if the user interacts with a draggable, resizable, etc. element since those interactions don't change focus. Ref #6642 Closes jquerygh-1614
1 parent 7df2f19 commit 548fbf5

File tree

2 files changed

+47
-18
lines changed

2 files changed

+47
-18
lines changed

tests/unit/autocomplete/core.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,4 +398,28 @@ asyncTest( "Search if the user retypes the same value (#7434)", function() {
398398
} );
399399
} );
400400

401+
asyncTest( "Close on click outside when focus remains", function() {
402+
expect( 2 );
403+
404+
var element = $( "#autocomplete" ).autocomplete( {
405+
source: [ "java", "javascript" ],
406+
delay: 0
407+
} );
408+
var menu = element.autocomplete( "widget" );
409+
410+
$( "body" ).on( "mousedown", function( event ) {
411+
event.preventDefault();
412+
} );
413+
414+
element.val( "j" ).autocomplete( "search", "j" );
415+
setTimeout(function() {
416+
ok( menu.is( ":visible" ), "menu displays initially" );
417+
$( "body" ).simulate( "mousedown" );
418+
setTimeout(function() {
419+
ok( menu.is( ":hidden" ), "menu closes after clicking elsewhere" );
420+
start();
421+
} );
422+
} );
423+
} );
424+
401425
} );

ui/widgets/autocomplete.js

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -245,24 +245,6 @@ $.widget( "ui.autocomplete", {
245245
this.element.trigger( "focus" );
246246
}
247247
} );
248-
249-
// Clicking on the scrollbar causes focus to shift to the body
250-
// but we can't detect a mouseup or a click immediately afterward
251-
// so we have to track the next mousedown and close the menu if
252-
// the user clicks somewhere outside of the autocomplete
253-
var menuElement = this.menu.element[ 0 ];
254-
if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
255-
this._delay( function() {
256-
var that = this;
257-
this.document.one( "mousedown", function( event ) {
258-
if ( event.target !== that.element[ 0 ] &&
259-
event.target !== menuElement &&
260-
!$.contains( menuElement, event.target ) ) {
261-
that.close();
262-
}
263-
} );
264-
} );
265-
}
266248
},
267249
menufocus: function( event, ui ) {
268250
var label, item;
@@ -368,6 +350,20 @@ $.widget( "ui.autocomplete", {
368350
}
369351
},
370352

353+
_isEventTargetInWidget: function( event ) {
354+
var menuElement = this.menu.element[ 0 ];
355+
356+
return event.target === this.element[ 0 ] ||
357+
event.target === menuElement ||
358+
$.contains( menuElement, event.target );
359+
},
360+
361+
_closeOnClickOutside: function( event ) {
362+
if ( !this._isEventTargetInWidget( event ) ) {
363+
this.close();
364+
}
365+
},
366+
371367
_appendTo: function() {
372368
var element = this.options.appendTo;
373369

@@ -496,6 +492,10 @@ $.widget( "ui.autocomplete", {
496492
},
497493

498494
_close: function( event ) {
495+
496+
// Remove the handler that closes the menu on outside clicks
497+
this._off( this.document, "mousedown" );
498+
499499
if ( this.menu.element.is( ":visible" ) ) {
500500
this.menu.element.hide();
501501
this.menu.blur();
@@ -546,6 +546,11 @@ $.widget( "ui.autocomplete", {
546546
if ( this.options.autoFocus ) {
547547
this.menu.next();
548548
}
549+
550+
// Listen for interactions outside of the widget (#6642)
551+
this._on( this.document, {
552+
mousedown: "_closeOnClickOutside"
553+
} );
549554
},
550555

551556
_resizeMenu: function() {

0 commit comments

Comments
 (0)