Skip to content

Commit 67d49f5

Browse files
committed
Overhaul tooltip implementation. Avoid queuing and other problems by
creating the tooltip element on the fly, never reusing it. Use aria-describedby attribute to find the associated tooltip again. Allows customizing animations much easier (just replace fadeIn/fadeOut), still open. Updated demos and visual test to replace now-missing .widget() method. Added tooltipClass for that.
1 parent bc65675 commit 67d49f5

File tree

5 files changed

+95
-81
lines changed

5 files changed

+95
-81
lines changed

demos/tooltip/custom-animation.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
<link type="text/css" href="../demos.css" rel="stylesheet" />
1212
<script type="text/javascript">
1313
$(function() {
14+
// TODO overhaul this with custom animation API
1415
$(".demo").tooltip({
1516
open: function() {
16-
$(this).tooltip("widget").stop(false, true).hide().slideDown();
17+
$(".ui-tooltip").stop(false, true).hide().slideDown();
1718
},
1819
close: function() {
19-
$(this).tooltip("widget").stop(false, true).show().slideUp();
20+
$(".ui-tooltip").stop(false, false).show().slideUp(function() {
21+
$(this).remove();
22+
});
2023
}
2124
});
2225
});

demos/tooltip/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ <h4>Examples</h4>
1414
<li><a href="tracking.html">Track the mouse</a></li>
1515
<li><a href="custom-animation.html">Custom animation</a></li>
1616
<li><a href="delegation-mixbag.html">Delegation Mixbag</a></li>
17+
<li><a href="lots.html">Lots</a></li>
1718
</ul>
1819
</div>
1920

demos/tooltip/tracking.html

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,19 @@
1313
$(function() {
1414
$(".demo").tooltip({
1515
open: function() {
16-
var tooltip = $(this).tooltip("widget");
17-
$(document).mousemove(function(event) {
18-
tooltip.position({
19-
my: "left center",
20-
at: "right center",
21-
offset: "25 25",
16+
var tooltip = $( ".ui-tooltip" );
17+
$(document).mousemove(function( event ) {
18+
tooltip.position( {
19+
my: "left+25 center",
20+
at: "right+25 center",
2221
of: event
2322
});
2423
})
2524
// trigger once to override element-relative positioning
2625
.mousemove();
2726
},
2827
close: function() {
29-
$(document).unbind("mousemove");
28+
$(document).unbind( "mousemove" );
3029
}
3130
});
3231
});

tests/visual/tooltip/tooltip.html

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,32 @@
1010
<script type="text/javascript" src="../../../ui/jquery.ui.position.js"></script>
1111
<script type="text/javascript" src="../../../ui/jquery.ui.button.js"></script>
1212
<script type="text/javascript" src="../../../ui/jquery.ui.tooltip.js"></script>
13+
<!--
1314
<script type="text/javascript" src="http://jqueryui.com/themeroller/themeswitchertool/"></script>
15+
-->
1416
<script type="text/javascript">
1517
$(function() {
1618
$.fn.themeswitcher && $('<div/>').css({
1719
position: "absolute",
1820
right: 10,
1921
top: 10
2022
}).appendTo(document.body).themeswitcher();
21-
23+
2224
function enable() {
2325
// default
2426
$("#context1, form, #childish").tooltip();
2527

2628
// custom class, replaces ui-widget-content
27-
$("#context2").tooltip().each(function() {
28-
$(this).tooltip("widget").addClass("ui-widget-header");
29-
})
30-
$("#right1").tooltip().tooltip("widget").addClass("ui-state-error");
29+
$("#context2").tooltip({
30+
tooltipClass: "ui-widget-header"
31+
});
32+
$("#right1").tooltip({
33+
tooltipClass: "ui-state-error"
34+
});
3135

3236
// synchronous content
3337
$("#footnotes").tooltip({
34-
items: "[href^=#]",
38+
items: "[href^='#']",
3539
content: function() {
3640
return $($(this).attr("href")).html();
3741
}
@@ -64,12 +68,13 @@
6468

6569
// custom position
6670
$("#right2").tooltip({
71+
tooltipClass: "ui-state-highlight",
6772
position: {
6873
my: "center top",
6974
at: "center bottom",
7075
offset: "0 10"
7176
}
72-
}).tooltip("widget").addClass("ui-state-highlight");
77+
});
7378

7479
$("#button1").button();
7580
$("#button2").button({
@@ -94,12 +99,12 @@
9499
enable();
95100

96101
$("#disable").toggle(function() {
97-
$("*").tooltip("disable");
102+
$(":ui-tooltip").tooltip("disable");
98103
}, function() {
99-
$("*").tooltip("enable");
104+
$(":ui-tooltip").tooltip("enable");
100105
});
101106
$("#toggle").toggle(function() {
102-
$("*").tooltip("destroy");
107+
$(":ui-tooltip").tooltip("destroy");
103108
}, function() {
104109
enable();
105110
});

ui/jquery.ui.tooltip.js

Lines changed: 68 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ var increments = 0;
1818

1919
$.widget("ui.tooltip", {
2020
options: {
21+
tooltipClass: null,
2122
items: "[title]",
2223
content: function() {
23-
return $(this).attr("title");
24+
return $( this ).attr( "title" );
2425
},
2526
position: {
2627
my: "left center",
@@ -29,109 +30,114 @@ $.widget("ui.tooltip", {
2930
}
3031
},
3132
_create: function() {
32-
var self = this;
33-
this.tooltip = $("<div></div>")
34-
.attr("id", "ui-tooltip-" + increments++)
35-
.attr("role", "tooltip")
36-
.attr("aria-hidden", "true")
37-
.addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content")
38-
.appendTo(document.body)
39-
.hide();
40-
this.tooltipContent = $("<div></div>")
41-
.addClass("ui-tooltip-content")
42-
.appendTo(this.tooltip);
43-
this.opacity = this.tooltip.css("opacity");
44-
this.element
45-
.bind("focus.tooltip mouseover.tooltip", function(event) {
46-
self.open( event );
47-
})
48-
.bind("blur.tooltip mouseout.tooltip", function(event) {
49-
self.close( event );
50-
});
33+
this._bind( {
34+
mouseover: "open",
35+
focusin: "open"
36+
});
5137
},
5238

5339
enable: function() {
5440
this.options.disabled = false;
5541
},
5642

5743
disable: function() {
44+
// only set option, disable element style changes
5845
this.options.disabled = true;
5946
},
6047

61-
_destroy: function() {
62-
this.tooltip.remove();
63-
},
64-
65-
widget: function() {
66-
return this.element.pushStack(this.tooltip.get());
67-
},
68-
6948
open: function(event) {
7049
var target = $(event && event.target || this.element).closest(this.options.items);
7150
if ( !target.length ) {
7251
return;
7352
}
74-
// already visible? possible when both focus and mouseover events occur
75-
if (this.current && this.current[0] == target[0])
76-
return;
7753
var self = this;
78-
this.current = target;
79-
this.currentTitle = target.attr("title");
54+
if ( !target.data("tooltip-title") ) {
55+
target.data("tooltip-title", target.attr("title"));
56+
}
8057
var content = this.options.content.call(target[0], function(response) {
8158
// IE may instantly serve a cached response, need to give it a chance to finish with _open before that
8259
setTimeout(function() {
83-
// ignore async responses that come in after the tooltip is already hidden
84-
if (self.current == target)
60+
// when undefined, it got removeAttr, then ignore (ajax response)
61+
// intially its an empty string, so not undefined
62+
// TODO is there a better approach to enable ajax tooltips to have two updates?
63+
if (target.attr( "aria-describedby" ) !== undefined) {
8564
self._open(event, target, response);
65+
}
8666
}, 13);
8767
});
8868
if (content) {
8969
self._open(event, target, content);
9070
}
9171
},
9272

93-
_open: function(event, target, content) {
94-
if (!content)
73+
_open: function( event, target, content ) {
74+
if ( !content )
9575
return;
96-
76+
9777
target.attr("title", "");
98-
99-
if (this.options.disabled)
78+
79+
if ( this.options.disabled )
10080
return;
101-
102-
this.tooltipContent.html(content);
103-
this.tooltip.css({
104-
top: 0,
105-
left: 0
106-
}).show().position( $.extend({
81+
82+
// ajaxy tooltip can update an existing one
83+
var tooltip = this._find( target );
84+
if (!tooltip.length) {
85+
tooltip = this._tooltip();
86+
target.attr( "aria-describedby", tooltip.attr( "id" ) );
87+
}
88+
tooltip.find(".ui-tooltip-content").html( content );
89+
tooltip.position( $.extend({
10790
of: target
108-
}, this.options.position )).hide();
109-
110-
this.tooltip.attr("aria-hidden", "false");
111-
target.attr("aria-describedby", this.tooltip.attr("id"));
91+
}, this.options.position ) ).hide();
92+
11293

113-
this.tooltip.stop(false, true).fadeIn();
94+
tooltip.fadeIn();
11495

11596
this._trigger( "open", event );
97+
98+
this._bind( target, {
99+
mouseleave: "close",
100+
blur: "close"
101+
});
116102
},
117103

118-
close: function(event) {
119-
if (!this.current)
120-
return;
121-
122-
var current = this.current;
123-
this.current = null;
124-
current.attr("title", this.currentTitle);
104+
close: function( event ) {
105+
var target = $( event && event.currentTarget || this.element );
106+
target.attr( "title", target.data( "tooltip-title" ) );
125107

126-
if (this.options.disabled)
108+
if ( this.options.disabled )
127109
return;
110+
111+
var tooltip = this._find( target );
112+
target.removeAttr( "aria-describedby" );
128113

129-
current.removeAttr("aria-describedby");
130-
this.tooltip.attr("aria-hidden", "true");
114+
tooltip.fadeOut( function() {
115+
$( this ).remove();
116+
});
131117

132-
this.tooltip.stop(false, true).fadeOut();
118+
target.unbind( "mouseleave.tooltip blur.tooltip" );
133119

134120
this._trigger( "close", event );
121+
},
122+
123+
_tooltip: function() {
124+
var tooltip = $( "<div></div>" )
125+
.attr( "id", "ui-tooltip-" + increments++ )
126+
.attr( "role", "tooltip" )
127+
.addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content" );
128+
if (this.options.tooltipClass) {
129+
tooltip.addClass(this.options.tooltipClass);
130+
}
131+
$( "<div></div>" )
132+
.addClass( "ui-tooltip-content" )
133+
.appendTo( tooltip );
134+
tooltip.appendTo( document.body );
135+
return tooltip;
136+
},
137+
138+
_find: function( target ) {
139+
var id = target.attr( "aria-describedby" );
140+
return id ? $( document.getElementById( id ) ) : $();
135141
}
136142
});
137143

0 commit comments

Comments
 (0)