Show Tooltip Only When Text Overflows
title is an attribute to represent advisory information related to the element it belongs to, typically presented as a tooltip. Sometimes we also use the title attribute to show omitted text when it's not suitable to wrap text and it overflows the container.
And here comes the question. If we simply add the title attribute, the tooltip will show regardless of whether the text overflows, while what we want is to show the tooltip only if the text overflows.
To achieve that, we may use jQuery with jQuery UI, or jQuery only, or even with vanilla JavaScript.
-
Firstly, if we do not want tooltip to be shown when text fits its container, we should
- set title attribute with no value;
- or not add the title attribute at all.
Otherwise we cannot control its behavior as browsers natively show tooltips for elements with title attribute.
Though we still need to mark elements which need to show tooltips when text within them overflow. For example, adding
data-title
attribute, usingtitle
attribute without value, or using a class name.<!-- title attribute with no value --> <div><span title>Long Text Really Long</span></div> <!-- or data-title attribute with no value --> <div><span data-title>Long Text Really Long</span></div> <!-- or data-title attribute with value --> <div><span data-title="Long Text Really Long">Long Text Really Long</span></div> <!-- or title attribute with value if using jQuery UI Tooltip widget --> <!-- jQuery UI Tooltip widget removes content in title attribute when hovered. --> <div><span title="Long Text Really Long">Long Text Really Long</span></div>
-
Then we may store tooltip content in another attribute such as
data-title
or not use an attribute at all if all we want to show in a tooltip is the text content of the element.DOM provides different ways for accessing data stored in different places, assuming the element we are dealing with is named as
el
:-
- Accessing
title
attribute: - via
el.title
, or$(el).attr('title')
in jQuery;
- Accessing
-
- Accessing
data-title
attribute: - via
el.getAttribute('data-title')
, or$(el).data('title')
in jQuery;
- Accessing
-
- Accessing text content of the element:
- via
el.textContent
, or$(el).text()
in jQuery;
var title = el.title || el.getAttribute('data-title') || el.textContent; // or in jQuery var $el = $(el); var title = $el.attr('title') || $el.data('title') || $el.text();
-
-
Next, we need a way to tell whether the content in a container overflows.
To learn the sizing of an element, we have
Element.getBoundingClientRect()
,Element.clientWidth
,Element.offsetWidth
,Element.scrollWidth
, and their Height, Left, Top counterparts.offsetWidth
andoffsetHeight
relate to element's border-box,clientWidth
andclientHeight
relate to padding-box (without the size of scrollbar if any), andscrollWidth
andscrollHeight
relate to padding-box plus size of scrollbar and overflowed content.Here for comparing the width of the element itself and the width of the content of the element, we compare
scrollWidth
withclientWidth
, ifscrollWidth
is larger thanclientWidth
, the text overflows.var overflowed = container.scrollWidth > container.clientWidth;
-
Finally, show the tooltip only if the text overflows.
- Using jQuery and jQuery UI
$(document).tooltip({ items: '[title]', // the default // or '[data-title]' // depending on HTML structure content: function () { // The container should be a `display: block` element, // as overflow means a `display: inline` element excedes // the size of a `display: block` element. var container = this; // or `this.parentNode` // depending on where `title` attribute is set // In any case, the element with `display: block`. var title = this.title || this.getAttribute('data-title') || this.textContent; var overflowed = container.scrollWidth > container.clientWidth; // Return an empty string as content won't show the tooltip return overflowed ? title : ''; } });
- Using jQuery only
// filter on '[title]' or '[data-title]' depending on HTML structure $(document) .on('mouseover', '[title]', function () { var container = this; // or `this.parentNode` var title = this.title || this.getAttribute('data-title') || this.textContent; var overflowed = container.scrollWidth > container.clientWidth; this.title = overflowed ? title : ''; }) .on('mouseout', '[title]', function() { this.title = ''; });
- Using vanilla JavaScript
document.addEventListener('mouseover', function(event) { var target = event.target; // Check support for matches() at <http://caniuse.com/#feat=matchesselector> if (!target.matches('[title]')) return; var container = this; // or `this.parentNode` var title = target.title || target.getAttribute('data-title') || target.textContent; var overflowed = container.scrollWidth > container.clientWidth; target.title = overflowed ? title : ''; }); document.addEventListener('mouseout', function(event) { var target = event.target; // Check support for matches() at <http://caniuse.com/#feat=matchesselector> if (!target.matches('[title]')) return; // 'mouseout' event fires even when move from parent to child element if (event.relatedTarget.parentNode === target) return; target.title = ''; });
Some side notes here, in the examples above, to take advantage of event bubbling and delegated events, mouseover
and mouseout
events are used, there are also mouseenter
and mouseleave
events which are similar, but they do not bubble, thus not suitable for event delegation.
I thinks the ability to use event delegation is important here, as there may be many elements with tooltips on a page. What's more, they might be added and/or removed dynamically. Thus using a single event handler for dealing with all these tooltips definitely improves performance.
Besides, I also made a JSFiddle demo at https://jsfiddle.net/pallxk/ket3mpy6/.