From 8825d93dc877d182cf4a3fce37b6c2593cf08552 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Scott=20Gonz=C3=A1lez?= Date: Tue, 30 Sep 2014 09:44:34 -0400 Subject: [PATCH] Tooltip: Properly track hiding and closing for delegated tooltips Fixes #10602 Closes gh-1353 --- tests/unit/tooltip/tooltip_core.js | 48 +++++++++++++++++++++++ ui/tooltip.js | 62 +++++++++++++++++------------- 2 files changed, 84 insertions(+), 26 deletions(-) diff --git a/tests/unit/tooltip/tooltip_core.js b/tests/unit/tooltip/tooltip_core.js index 760ffeed2..2e029c7b0 100644 --- a/tests/unit/tooltip/tooltip_core.js +++ b/tests/unit/tooltip/tooltip_core.js @@ -174,4 +174,52 @@ asyncTest( "destroy during hide animation; only one close event", function() { }); }); +// http://bugs.jqueryui.com/ticket/10602 +asyncTest( "multiple active delegated tooltips", function() { + expect( 1 ); + + var anchor = $( "#tooltipped1" ), + input = anchor.next(), + actions = []; + + $( document ).tooltip({ + show: false, + hide: false, + open: function( event, ui ) { + actions.push( "open:" + ui.tooltip.text() ); + }, + close: function( event, ui ) { + actions.push( "close:" + ui.tooltip.text() ); + } + }); + + function step1() { + anchor.simulate( "mouseover" ); + setTimeout( step2 ); + } + + function step2() { + input.simulate( "focus" ); + setTimeout( step3 ); + } + + function step3() { + input.simulate( "blur" ); + setTimeout( step4 ); + } + + function step4() { + anchor.simulate( "mouseout" ); + deepEqual( actions, [ + "open:anchortitle", + "open:inputtitle", + "close:inputtitle", + "close:anchortitle" + ], "Both tooltips open and close" ); + start(); + } + + step1(); +}); + }( jQuery ) ); diff --git a/ui/tooltip.js b/ui/tooltip.js index 048b6324a..180c9f8e2 100644 --- a/ui/tooltip.js +++ b/ui/tooltip.js @@ -86,6 +86,7 @@ return $.widget( "ui.tooltip", { // IDs of generated tooltips, needed for destroy this.tooltips = {}; + // IDs of parent tooltips where we removed the title attribute this.parents = {}; @@ -117,8 +118,8 @@ return $.widget( "ui.tooltip", { this._super( key, value ); if ( key === "content" ) { - $.each( this.tooltips, function( id, element ) { - that._updateContent( element ); + $.each( this.tooltips, function( id, tooltipData ) { + that._updateContent( tooltipData.element ); }); } }, @@ -127,9 +128,9 @@ return $.widget( "ui.tooltip", { var that = this; // close open tooltips - $.each( this.tooltips, function( id, element ) { + $.each( this.tooltips, function( id, tooltipData ) { var event = $.Event( "blur" ); - event.target = event.currentTarget = element[0]; + event.target = event.currentTarget = tooltipData.element[ 0 ]; that.close( event, true ); }); @@ -231,7 +232,7 @@ return $.widget( "ui.tooltip", { }, _open: function( event, target, content ) { - var tooltip, events, delayedShow, a11yContent, + var tooltipData, tooltip, events, delayedShow, a11yContent, positionOption = $.extend( {}, this.options.position ); if ( !content ) { @@ -240,9 +241,9 @@ return $.widget( "ui.tooltip", { // Content can be updated multiple times. If the tooltip already // exists, then just update the content and bail. - tooltip = this._find( target ); - if ( tooltip.length ) { - tooltip.find( ".ui-tooltip-content" ).html( content ); + tooltipData = this._find( target ); + if ( tooltipData ) { + tooltipData.tooltip.find( ".ui-tooltip-content" ).html( content ); return; } @@ -261,7 +262,8 @@ return $.widget( "ui.tooltip", { } } - tooltip = this._tooltip( target ); + tooltipData = this._tooltip( target ); + tooltip = tooltipData.tooltip; this._addDescribedBy( target, tooltip.attr( "id" ) ); tooltip.find( ".ui-tooltip-content" ).html( content ); @@ -296,8 +298,6 @@ return $.widget( "ui.tooltip", { }, this.options.position ) ); } - this.hiding = false; - this.closing = false; tooltip.hide(); this._show( tooltip, this.options.show ); @@ -343,13 +343,21 @@ return $.widget( "ui.tooltip", { }, close: function( event ) { - var that = this, + var tooltip, + that = this, target = $( event ? event.currentTarget : this.element ), - tooltip = this._find( target ); + tooltipData = this._find( target ); + + // The tooltip may already be closed + if ( !tooltipData ) { + return; + } + + tooltip = tooltipData.tooltip; // disabling closes the tooltip, so we need to track when we're closing // to avoid an infinite loop in case the tooltip becomes disabled on close - if ( this.closing ) { + if ( tooltipData.closing ) { return; } @@ -364,12 +372,10 @@ return $.widget( "ui.tooltip", { this._removeDescribedBy( target ); - this.hiding = true; + tooltipData.hiding = true; tooltip.stop( true ); this._hide( tooltip, this.options.hide, function() { that._removeTooltip( $( this ) ); - this.hiding = false; - this.closing = false; }); target.removeData( "ui-tooltip-open" ); @@ -388,10 +394,10 @@ return $.widget( "ui.tooltip", { }); } - this.closing = true; + tooltipData.closing = true; this._trigger( "close", event, { tooltip: tooltip } ); - if ( !this.hiding ) { - this.closing = false; + if ( !tooltipData.hiding ) { + tooltipData.closing = false; } }, @@ -407,13 +413,16 @@ return $.widget( "ui.tooltip", { .appendTo( tooltip ); tooltip.appendTo( this.document[0].body ); - this.tooltips[ id ] = element; - return tooltip; + + return this.tooltips[ id ] = { + element: element, + tooltip: tooltip + }; }, _find: function( target ) { var id = target.data( "ui-tooltip-id" ); - return id ? $( "#" + id ) : $(); + return id ? this.tooltips[ id ] : null; }, _removeTooltip: function( tooltip ) { @@ -425,10 +434,11 @@ return $.widget( "ui.tooltip", { var that = this; // close open tooltips - $.each( this.tooltips, function( id, element ) { + $.each( this.tooltips, function( id, tooltipData ) { // Delegate to close method to handle common cleanup - var event = $.Event( "blur" ); - event.target = event.currentTarget = element[0]; + var event = $.Event( "blur" ), + element = tooltipData.element; + event.target = event.currentTarget = element[ 0 ]; that.close( event, true ); // Remove immediately; destroying an open tooltip doesn't use the -- 2.39.5