Skip to content

Commit

Permalink
Overhaul tooltip implementation. Avoid queuing and other problems by
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jzaefferer committed Apr 13, 2011
1 parent bc65675 commit 67d49f5
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 81 deletions.
7 changes: 5 additions & 2 deletions demos/tooltip/custom-animation.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
<link type="text/css" href="../demos.css" rel="stylesheet" />
<script type="text/javascript">
$(function() {
// TODO overhaul this with custom animation API
$(".demo").tooltip({
open: function() {
$(this).tooltip("widget").stop(false, true).hide().slideDown();
$(".ui-tooltip").stop(false, true).hide().slideDown();
},
close: function() {
$(this).tooltip("widget").stop(false, true).show().slideUp();
$(".ui-tooltip").stop(false, false).show().slideUp(function() {
$(this).remove();
});
}
});
});
Expand Down
1 change: 1 addition & 0 deletions demos/tooltip/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ <h4>Examples</h4>
<li><a href="tracking.html">Track the mouse</a></li>
<li><a href="custom-animation.html">Custom animation</a></li>
<li><a href="delegation-mixbag.html">Delegation Mixbag</a></li>
<li><a href="lots.html">Lots</a></li>
</ul>
</div>

Expand Down
13 changes: 6 additions & 7 deletions demos/tooltip/tracking.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,19 @@
$(function() {
$(".demo").tooltip({
open: function() {
var tooltip = $(this).tooltip("widget");
$(document).mousemove(function(event) {
tooltip.position({
my: "left center",
at: "right center",
offset: "25 25",
var tooltip = $( ".ui-tooltip" );
$(document).mousemove(function( event ) {
tooltip.position( {
my: "left+25 center",
at: "right+25 center",
of: event
});
})
// trigger once to override element-relative positioning
.mousemove();
},
close: function() {
$(document).unbind("mousemove");
$(document).unbind( "mousemove" );
}
});
});
Expand Down
25 changes: 15 additions & 10 deletions tests/visual/tooltip/tooltip.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,32 @@
<script type="text/javascript" src="../../../ui/jquery.ui.position.js"></script>
<script type="text/javascript" src="../../../ui/jquery.ui.button.js"></script>
<script type="text/javascript" src="../../../ui/jquery.ui.tooltip.js"></script>
<!--
<script type="text/javascript" src="http://jqueryui.com/themeroller/themeswitchertool/"></script>
-->
<script type="text/javascript">
$(function() {
$.fn.themeswitcher && $('<div/>').css({
position: "absolute",
right: 10,
top: 10
}).appendTo(document.body).themeswitcher();

function enable() {
// default
$("#context1, form, #childish").tooltip();

// custom class, replaces ui-widget-content
$("#context2").tooltip().each(function() {
$(this).tooltip("widget").addClass("ui-widget-header");
})
$("#right1").tooltip().tooltip("widget").addClass("ui-state-error");
$("#context2").tooltip({
tooltipClass: "ui-widget-header"
});
$("#right1").tooltip({
tooltipClass: "ui-state-error"
});

// synchronous content
$("#footnotes").tooltip({
items: "[href^=#]",
items: "[href^='#']",
content: function() {
return $($(this).attr("href")).html();
}
Expand Down Expand Up @@ -64,12 +68,13 @@

// custom position
$("#right2").tooltip({
tooltipClass: "ui-state-highlight",
position: {
my: "center top",
at: "center bottom",
offset: "0 10"
}
}).tooltip("widget").addClass("ui-state-highlight");
});

$("#button1").button();
$("#button2").button({
Expand All @@ -94,12 +99,12 @@
enable();

$("#disable").toggle(function() {
$("*").tooltip("disable");
$(":ui-tooltip").tooltip("disable");
}, function() {
$("*").tooltip("enable");
$(":ui-tooltip").tooltip("enable");
});
$("#toggle").toggle(function() {
$("*").tooltip("destroy");
$(":ui-tooltip").tooltip("destroy");
}, function() {
enable();
});
Expand Down
130 changes: 68 additions & 62 deletions ui/jquery.ui.tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ var increments = 0;

$.widget("ui.tooltip", {
options: {
tooltipClass: null,
items: "[title]",
content: function() {
return $(this).attr("title");
return $( this ).attr( "title" );
},
position: {
my: "left center",
Expand All @@ -29,109 +30,114 @@ $.widget("ui.tooltip", {
}
},
_create: function() {
var self = this;
this.tooltip = $("<div></div>")
.attr("id", "ui-tooltip-" + increments++)
.attr("role", "tooltip")
.attr("aria-hidden", "true")
.addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content")
.appendTo(document.body)
.hide();
this.tooltipContent = $("<div></div>")
.addClass("ui-tooltip-content")
.appendTo(this.tooltip);
this.opacity = this.tooltip.css("opacity");
this.element
.bind("focus.tooltip mouseover.tooltip", function(event) {
self.open( event );
})
.bind("blur.tooltip mouseout.tooltip", function(event) {
self.close( event );
});
this._bind( {
mouseover: "open",
focusin: "open"
});
},

enable: function() {
this.options.disabled = false;
},

disable: function() {
// only set option, disable element style changes
this.options.disabled = true;
},

_destroy: function() {
this.tooltip.remove();
},

widget: function() {
return this.element.pushStack(this.tooltip.get());
},

open: function(event) {
var target = $(event && event.target || this.element).closest(this.options.items);
if ( !target.length ) {
return;
}
// already visible? possible when both focus and mouseover events occur
if (this.current && this.current[0] == target[0])
return;
var self = this;
this.current = target;
this.currentTitle = target.attr("title");
if ( !target.data("tooltip-title") ) {
target.data("tooltip-title", target.attr("title"));
}
var content = this.options.content.call(target[0], function(response) {
// IE may instantly serve a cached response, need to give it a chance to finish with _open before that
setTimeout(function() {
// ignore async responses that come in after the tooltip is already hidden
if (self.current == target)
// when undefined, it got removeAttr, then ignore (ajax response)
// intially its an empty string, so not undefined
// TODO is there a better approach to enable ajax tooltips to have two updates?
if (target.attr( "aria-describedby" ) !== undefined) {
self._open(event, target, response);
}
}, 13);
});
if (content) {
self._open(event, target, content);
}
},

_open: function(event, target, content) {
if (!content)
_open: function( event, target, content ) {
if ( !content )
return;

target.attr("title", "");
if (this.options.disabled)

if ( this.options.disabled )
return;

this.tooltipContent.html(content);
this.tooltip.css({
top: 0,
left: 0
}).show().position( $.extend({

// ajaxy tooltip can update an existing one
var tooltip = this._find( target );
if (!tooltip.length) {
tooltip = this._tooltip();
target.attr( "aria-describedby", tooltip.attr( "id" ) );
}
tooltip.find(".ui-tooltip-content").html( content );
tooltip.position( $.extend({
of: target
}, this.options.position )).hide();

this.tooltip.attr("aria-hidden", "false");
target.attr("aria-describedby", this.tooltip.attr("id"));
}, this.options.position ) ).hide();


this.tooltip.stop(false, true).fadeIn();
tooltip.fadeIn();

this._trigger( "open", event );

this._bind( target, {
mouseleave: "close",
blur: "close"
});
},

close: function(event) {
if (!this.current)
return;

var current = this.current;
this.current = null;
current.attr("title", this.currentTitle);
close: function( event ) {
var target = $( event && event.currentTarget || this.element );
target.attr( "title", target.data( "tooltip-title" ) );

if (this.options.disabled)
if ( this.options.disabled )
return;

var tooltip = this._find( target );
target.removeAttr( "aria-describedby" );

current.removeAttr("aria-describedby");
this.tooltip.attr("aria-hidden", "true");
tooltip.fadeOut( function() {
$( this ).remove();
});

this.tooltip.stop(false, true).fadeOut();
target.unbind( "mouseleave.tooltip blur.tooltip" );

this._trigger( "close", event );
},

_tooltip: function() {
var tooltip = $( "<div></div>" )
.attr( "id", "ui-tooltip-" + increments++ )
.attr( "role", "tooltip" )
.addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content" );
if (this.options.tooltipClass) {
tooltip.addClass(this.options.tooltipClass);
}
$( "<div></div>" )
.addClass( "ui-tooltip-content" )
.appendTo( tooltip );
tooltip.appendTo( document.body );
return tooltip;
},

_find: function( target ) {
var id = target.attr( "aria-describedby" );
return id ? $( document.getElementById( id ) ) : $();
}
});

Expand Down

0 comments on commit 67d49f5

Please sign in to comment.