From 68085bd2e992d4eadbbcf9094e64fc879fb55fed Mon Sep 17 00:00:00 2001 From: Ryan Oriecuia Date: Thu, 12 Jan 2017 11:16:20 -0800 Subject: [PATCH 1/2] autocomplete: fix for IE scrolling issues IE11 and scrolling autocompletes didn't get along great; this should help fix their relationship. When you click on an autocomplete scrollbar in IE11, the menu temporarily gains focus, which caused a couple problems. 1. Depending on how long you clicked, the dropdown could close. 2. Scrolling down by clicking the scrollbar's down arrow would misbehave. The list would pop back up to the top with the first item selected. We can fix both problems by modifying the focus/blur handling a bit. 1. There is a flag to instruct the control to ignore blurs, but it was getting cleared too quickly; when the code refocused the input after it was blurred, IE would send *another* blur event, which wasn't getting ignored and would close the dropdown. We now wait for the focus/blur pair to process before clearing the flag. 2. We remove the tabindex from the dropdown menu, which prevents menu's focus handler from firing. When you focus a menu, it will select the first menu item if none are selected. Selecting a menu item will scroll it into view if it's not visible. This combination of behaviors was causing the strange behavior when attempting to scroll down. I couldn't figure out a way to write a unit test for this, since it's IE only and seems to require user interaction. You can verify the previous behavior (and the fix) on `demos/autocomplete/maxheight.html` Fixes #9638 --- ui/widgets/autocomplete.js | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/ui/widgets/autocomplete.js b/ui/widgets/autocomplete.js index 079b0dab07b..521cbc46326 100644 --- a/ui/widgets/autocomplete.js +++ b/ui/widgets/autocomplete.js @@ -220,6 +220,16 @@ $.widget( "ui.autocomplete", { role: null } ) .hide() + + // Remove menu's tabindex to fix a scrolling problem in IE. + // The menu will receive focus when a user clicks the scrollbar, + // which will scroll the menu to the active item (by default, + // the top item) which makes scrolling down harder than it + // ought to be. + // You can't tab into the dropdown anyway (blurring the text + // input will close the dropdown) so this won't break keyboard + // navigation. + .attr( "tabindex", "" ) .menu( "instance" ); this._addClass( this.menu.element, "ui-autocomplete", "ui-front" ); @@ -233,17 +243,21 @@ $.widget( "ui.autocomplete", { // so we set a flag to know when we should ignore the blur event this.cancelBlur = true; this._delay( function() { - delete this.cancelBlur; - // Support: IE 8 only - // Right clicking a menu item or selecting text from the menu items will - // result in focus moving out of the input. However, we've already received - // and ignored the blur event because of the cancelBlur flag set above. So - // we restore focus to ensure that the menu closes properly based on the user's - // next actions. + // Support: IE + // Right clicking a menu item, selecting text from the menu items, or clicking + // the scrollbar will result in focus moving out of the input. However, we've + // already received and ignored the blur event because of the cancelBlur flag + // set above. So we restore focus to ensure that the menu closes properly based + // on the user's next actions. + // Note that the focus event can itself raise another blur event, so we need + // to delay the removal of the cancelBlur flag. if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) { this.element.trigger( "focus" ); } + this._delay( function() { + delete this.cancelBlur; + } ); } ); }, menufocus: function( event, ui ) { From 6b16d65a67cc27f1f01019212bcba6f08855a659 Mon Sep 17 00:00:00 2001 From: Ryan Oriecuia Date: Fri, 13 Jan 2017 14:45:18 -0800 Subject: [PATCH 2/2] autocomplete: fix for IE11/Edge scrolling issues IE11 and Edge will not prevent focus when you `preventDefault` on a mousedown events; this was causing some undesireable behavior: 1. Depending on how long you clicked, the dropdown could close (IE11) 2. Scrolling down by clicking the scrollbar's down arrow would misbehave. The list would pop back up to the top with the first item selected. (IE11, Edge) Both these problems are fixed by making the dropdown un-focusable (by setting the IE/Edge-only `unselectable` attribute to `on`). I couldn't figure out a way to write a unit test for this, since it's IE only and seems to require user interaction. You can verify the previous behavior (and the fix) on demos/autocomplete/maxheight.html Fixes #9638 --- ui/widgets/autocomplete.js | 46 +++++++++----------------------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/ui/widgets/autocomplete.js b/ui/widgets/autocomplete.js index 521cbc46326..52a832b4e2e 100644 --- a/ui/widgets/autocomplete.js +++ b/ui/widgets/autocomplete.js @@ -200,11 +200,6 @@ $.widget( "ui.autocomplete", { this.previous = this._value(); }, blur: function( event ) { - if ( this.cancelBlur ) { - delete this.cancelBlur; - return; - } - clearTimeout( this.searching ); this.close( event ); this._change( event ); @@ -221,44 +216,23 @@ $.widget( "ui.autocomplete", { } ) .hide() - // Remove menu's tabindex to fix a scrolling problem in IE. - // The menu will receive focus when a user clicks the scrollbar, - // which will scroll the menu to the active item (by default, - // the top item) which makes scrolling down harder than it - // ought to be. - // You can't tab into the dropdown anyway (blurring the text - // input will close the dropdown) so this won't break keyboard - // navigation. - .attr( "tabindex", "" ) + // Support: IE 11, Edge + // For other browsers, we preventDefault() on the mousedown event + // to keep the dropdown from taking focus from the input. This doesn't + // work for IE/Edge, causing problems with selection and scrolling (#9638) + // Happily, IE and Edge support an "unselectable" attribute that + // prevents an element from receiving focus, exactly what we want here. + .attr( { + "unselectable": "on" + } ) .menu( "instance" ); this._addClass( this.menu.element, "ui-autocomplete", "ui-front" ); this._on( this.menu.element, { mousedown: function( event ) { - // prevent moving focus out of the text field + // Prevent moving focus out of the text field event.preventDefault(); - - // IE doesn't prevent moving focus even with event.preventDefault() - // so we set a flag to know when we should ignore the blur event - this.cancelBlur = true; - this._delay( function() { - - // Support: IE - // Right clicking a menu item, selecting text from the menu items, or clicking - // the scrollbar will result in focus moving out of the input. However, we've - // already received and ignored the blur event because of the cancelBlur flag - // set above. So we restore focus to ensure that the menu closes properly based - // on the user's next actions. - // Note that the focus event can itself raise another blur event, so we need - // to delay the removal of the cancelBlur flag. - if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) { - this.element.trigger( "focus" ); - } - this._delay( function() { - delete this.cancelBlur; - } ); - } ); }, menufocus: function( event, ui ) { var label, item;