Basically, I'm attaching an open
handler to my jQuery UI tooltip which performs some checks on the element that triggered the tooltip. What I've got so far:
$(document).tooltip({
open: function(e, ui) {
var el = e.toElement/* || e.relatedTarget*/;
console.log(el.offsetWidth, el.scrollWidth);
if (el.offsetWidth === el.scrollWidth) {
ui.tooltip.hide();
}
}
});
The check above prevents the tooltip from appearing unless the element is overflowing horizontally, part of a fluid layout. It works nicely in Chrome as you can see in this jsBin.
However, in Firefox, event.toElement
is undefined
. From reading threads around SO, I've thought that event.relatedTarget
would be a suitable substitute, but it is not. While event.toElement
references the currently hovered item, the event.relatedTarget
in a mouseover
references the element which the pointing device exited, which is the correct behavior as per W3C spec (similar to Chrome's event.fromTarget
).
I've also tried event.target
, event.currentTarget
and the this
reference but these point to document
as it is the node which has the tooltip event handlers bound to. Going through the Tooltip API page didn't help either.
I'm not sure whether I'm overlooking something extremely basic, or if I should try even less orthodox methods.
Is there a way to get a reference to the element that triggered the from inside the tooltip's open
handler which works in Firefox? Or is there some magic jQuery UI Tooltip option/method that can attain this desired behavior in a simpler/similar way?
This is a rather hackish solution, but here goes the temporary fix which I just found. edit: after having finished the 1st cross-browser solution below, it is not so hackish at all. The #1, #4 and #2 solution listed below should be usable.
jQuery Event objects have a hidden originalEvent
property, which in the question's case is a references a native mouseover
event. Therefore event.originalEvent.target
can be used for both Chrome and Firefox.
open: function(e, ui) {
var el = e.originalEvent.target;
if (el.offsetWidth === el.scrollWidth) {
ui.tooltip.hide();
}
}
When old IE support is concerned, you will have to use event.srcElement
when event.target
is not present.
var el = e.originalEvent.target || e.originalEvent.srcElement;
Finally, when there are nested elements inside of an element that triggers the tooltip, you will have to monkeypatch it by using the .closest()
method passing in the same filter as your tooltip's delegation selector (items
option, default to [title]:not([disabled])
as of UI 1.10.2):
var el = $(e.originalEvent.target || e.originalEvent.srcElement).closest($(this).tooltip('option', 'items'))[0];
This is basically what the Tooltip Widget does internally as shown here.
Alternative solution that doesn't require so many workarounds by using a simple DOM query:
var el = $('[aria-describedby="'+ui.tooltip[0].id+'"]')[0];
#3 Internal methods abuse
This should not be used, but by overriding the internal _open
method you get access to a target
jQuery object which contains the event's target element passed as parameter to it. Problem is, then you don't have access to its tooltip
widget not even through .tooltip('widget')
as it is "not created" yet though already present in the DOM. You can work around it using the internal _find
method which will perform a DOM query by ID and thus you can hide
it right after the show
animation would kick in, while its live cycle is not affected - it will be there and removed on mouseleave
as usual, but will have display:none
along all this cycle.
var bk_open = $.ui.tooltip.prototype._open;
$.ui.tooltip.prototype._open = function(event, target, content) {
bk_open.apply(this, arguments);
if (target[0].offsetWidth === target[0].scrollWidth) {
this._find(target).hide();
}
};
$(document).tooltip();
That DOM query with _find
is rather unnecessary, so we can also extend the internal _tooltip
method which returns a jQuery object containing the tooltip
element so we can use JS's lexical scope to save a reference to the tooltip element before our overridden _open
executes:
var tooltipproto = $.ui.tooltip.prototype,
bk_open = tooltipproto._open,
bk_tooltip = tooltipproto._tooltip,
$tooltip;
tooltipproto._open = function(event, target, content) {
bk_open.apply(this, arguments);
if (target[0].offsetWidth === target[0].scrollWidth) {
$tooltip.hide();
}
};
tooltipproto._tooltip = function(element) {
return ($tooltip = bk_tooltip.apply(this, arguments));
};
$(document).tooltip();
Of course, as the _tooltip
internal methods receives the target
as parameter and returns the tooltip
, one could do the entire operation just overriding this method, but as the tooltip
is show
n after this method returns, this would require a setTimeout(fn, 0)
possibly causing an undesirable flicker effect.
This is overly hackish, cumbersome and verbose for something so simple though.
"Clean" as in not using undocumented methods nor attributes nor prototype overriding nor DOM queries. Back to the first snippet, all we needed was a reference to the element that triggered the tooltip. This element is referenced by the this
inside of the content
function which is called prior to the open
handler, thus we can use lexical scope to store that reference a level above:
var el;
$(document).tooltip({
content: function() {
el = this;
return this.title;
},
open: function(e, ui) {
if (el.offsetWidth === el.scrollWidth) {
ui.tooltip.hide();
}
}
});
Note that my custom content
function in the snippet above removes jQuery's default HTML tags stripping (because I like using HTML inside the tooltips), but this may be an issue if you're dynamically populating the title attribute with user-inputted data, so in case you want to keep the original content
handler's functionality:
var el,
bk_content = $.ui.tooltip.prototype.options.content;
$(document).tooltip({
content: function() {
el = this;
return bk_content.apply(this, arguments);
},
open: function(e, ui) {
if (el.offsetWidth === el.scrollWidth) {
ui.tooltip.hide();
}
}
});
I'll open a ticket on jQuery UI bugtracker requesting this feature to be implemented meanwhile. Here's it:
Tooltip: Expose element which triggered the tooltip inside open/close handlers