diff --git a/tests/unit/autocomplete/core.js b/tests/unit/autocomplete/core.js index 41be5ae6a..9cfac3917 100644 --- a/tests/unit/autocomplete/core.js +++ b/tests/unit/autocomplete/core.js @@ -398,4 +398,28 @@ asyncTest( "Search if the user retypes the same value (#7434)", function() { } ); } ); +asyncTest( "Close on click outside when focus remains", function() { + expect( 2 ); + + var element = $( "#autocomplete" ).autocomplete( { + source: [ "java", "javascript" ], + delay: 0 + } ); + var menu = element.autocomplete( "widget" ); + + $( "body" ).on( "mousedown", function( event ) { + event.preventDefault(); + } ); + + element.val( "j" ).autocomplete( "search", "j" ); + setTimeout(function() { + ok( menu.is( ":visible" ), "menu displays initially" ); + $( "body" ).simulate( "mousedown" ); + setTimeout(function() { + ok( menu.is( ":hidden" ), "menu closes after clicking elsewhere" ); + start(); + } ); + } ); +} ); + } ); diff --git a/ui/widgets/autocomplete.js b/ui/widgets/autocomplete.js index 4067f871f..20d6d2fdd 100644 --- a/ui/widgets/autocomplete.js +++ b/ui/widgets/autocomplete.js @@ -245,24 +245,6 @@ $.widget( "ui.autocomplete", { this.element.trigger( "focus" ); } } ); - - // Clicking on the scrollbar causes focus to shift to the body - // but we can't detect a mouseup or a click immediately afterward - // so we have to track the next mousedown and close the menu if - // the user clicks somewhere outside of the autocomplete - var menuElement = this.menu.element[ 0 ]; - if ( !$( event.target ).closest( ".ui-menu-item" ).length ) { - this._delay( function() { - var that = this; - this.document.one( "mousedown", function( event ) { - if ( event.target !== that.element[ 0 ] && - event.target !== menuElement && - !$.contains( menuElement, event.target ) ) { - that.close(); - } - } ); - } ); - } }, menufocus: function( event, ui ) { var label, item; @@ -368,6 +350,20 @@ $.widget( "ui.autocomplete", { } }, + _isEventTargetInWidget: function( event ) { + var menuElement = this.menu.element[ 0 ]; + + return event.target === this.element[ 0 ] || + event.target === menuElement || + $.contains( menuElement, event.target ); + }, + + _closeOnClickOutside: function( event ) { + if ( !this._isEventTargetInWidget( event ) ) { + this.close(); + } + }, + _appendTo: function() { var element = this.options.appendTo; @@ -496,6 +492,10 @@ $.widget( "ui.autocomplete", { }, _close: function( event ) { + + // Remove the handler that closes the menu on outside clicks + this._off( this.document, "mousedown" ); + if ( this.menu.element.is( ":visible" ) ) { this.menu.element.hide(); this.menu.blur(); @@ -546,6 +546,11 @@ $.widget( "ui.autocomplete", { if ( this.options.autoFocus ) { this.menu.next(); } + + // Listen for interactions outside of the widget (#6642) + this._on( this.document, { + mousedown: "_closeOnClickOutside" + } ); }, _resizeMenu: function() {