Applying effects to the drop-down menu
By default, we get a fairly simplistic presentation of the drop-down menu containing suggestions for completion based on what we type. The menu is simply displayed, without much fuss. Which is fine, it gets the job done reliably. But, on the other hand, there is always something we can do to make the UI look more polished. It could be as simple as changing the autocomplete widget in your application to use some subtle effects while transitioning to a visible state.
Getting ready
Since what we're after here is really more of a visual presentation aspect of the widget, we're probably safe using any existing instance of the widget.
How to do it...
Let's build on the default implementation of the autocomplete widget to include some subtle animations for the drop-down menu.
( function( $, undefined ) { $.widget( "ab.autocomplete", $.ui.autocomplete, { _suggest: function( items ) { this._resetMenu(); this._renderMenu( this.menu.element, items ); this.menu.refresh(); this._resizeMenu(); this._positionMenu(); }, _resetMenu: function() { this.menu.element .empty() .zIndex( this.element.zIndex() + 1 ); }, _positionMenu: function() { var pos = $.extend( { of: this.element }, this.options.position ); this.menu.element.position( pos ); }, _resizeMenu: function() { var menu = this.menu, exclude = 0; target = Math.max( menu.element.width( "" ).outerWidth() + 1, this.element.outerWidth() ), excludeCSS = [ 'borderLeftWidth', 'borderRightWidth', 'paddingLeft', 'paddingRight' ]; if( menu.element.is( ":hidden" ) ) { menu.element.css( { display: "block", opacity: 0 } ); } $.each( excludeCSS , function( index, item ) { exclude += parseFloat( menu.element.css( item ) ); }); if ( menu.element.css( "opacity" ) == 0 ) { menu.element.animate({ width: target - exclude, opacity: 1 }, 300); } else{ menu.element.width( target - exclude ); } }, _close: function( event ) { var menu = this.menu; if ( menu.element.is( ":visible" ) ) { menu.element.fadeOut(); menu.blur(); this.isNewMenu = true; this._trigger( "close", event ); } } }); })( jQuery ); $(function() { var source = [ "First Item", "Second Item", "Third Item", "Fourth Item" ]; $( "#autocomplete" ).autocomplete({ source: source, }); });
If you start using this autocomplete widget by typing in the input element, you'll notice that the drop-down menu glides smoothly into view instead of just popping out abruptly. Also, when the menu is no longer needed, it fades out of existence.
How it works...
The autocomplete is being extended here so we can inject our customized animation functionality. But this time around, the changes are a little more involved, we're not merely extending _create()
with a few lines of code. There are a few methods buried deep in the autocomplete code that we need to extend. We've also introduced a few new methods of our own in the autocomplete widget.
The first method we're overriding is _suggest()
. The _suggest()
method is called by the autocomplete widget when the user has typed the minimum length of characters to perform a search. The original method takes care of everything that needs to happen in terms of rendering and displaying the drop-down menu. In our version of the method, we're just calling other methods of the widget. The job of _suggest()
is to orchestrate everything that takes place when a search happens. There are two logical steps taken here. First, the menu is rendered with the new content. Next, the menu is displayed, resized, and positioned. The latter is where the animation takes place.
We won't drill into the details of the _resetMenu()
and _positionMenu()
methods as those code snippets are taken from the original implementation for the most part. They just empty and position the menu, respectively.
The _resizeMenu()
method is where the actual animation takes place when the menu is displayed. This is a longer method because we have to perform a few calculations to pass into animate()
. The original implementation of _resizeMenu()
uses the outerWidth()
jQuery function to set the width of the menu. This is to get proper alignment with the input
element. However, we want to animate the width
change. So we must manually compute the inner width. The outer width values go in the exclude variable. The inner width is target - exclude
.
We check if the menu is already displayed before actually showing it, and before animating it. If the element isn't visible, we change the display
CSS property, but set the opacity
property to 0
. The reason we do this is that we need the element's box model dimensions in order to position it. But, we still haven't applied the animation effects to the menu. Here, we check if the opacity
property for the menu is at 0
. If not, that means the menu is already displayed and it would not make sense to re-animate it now. Otherwise, we execute the width and opacity animation.
Finally, the _close()
method replaces the original autocomplete _close()
implementation. The code is nearly the same as the original, except we do a basic fadeOut()
when the menu is closed, as opposed to merely hiding it.
Note
This extension of the autocomplete does not implement an option that will turn off this behavior. This is alright because the extension only does one thing—apply effects to the drop-down menu. So, to disable these effects, we can just disable extension. The extension of a widget is defined inside a function that calls itself. When the script first loads, the function is called, and the widget gets extended with the new behavior. We can disable the behavior part of the function that invokes itself.
(function( $, undefined ) { // Code that extends a jQuery UI widget... }); //( jQuery );