In this post, we’ll see how we can create a custom directive for displaying tooltip for any element if the text in the element goes beyond its width.
If we wish to display a tooltip, we’d wire up a mouseover
event to the element that we would like to display a tooltip on.
But, this task can get repetitive if we have to apply for other elements on the page or if we need to display it on other components across the application.
In this article, I’ll just make use of the title tag available in HTML to display the tooltip for element using no custom libraries for displaying the tooltip.
v-tooltip directive
If you are new to Vue directives, check out how to create a custom directive in VueJS.
Here is the v-tooltip directive.
export default { directives: { tooltip: { bind: function (el) { el.addEventListener('mouseover', function (evt) { let targetEl = evt.target; if (targetEl.offsetWidth < targetEl.scrollWidth) { targetEl.setAttribute('title', evt.target.textContent); } else { targetEl.hasAttribute('title') && targetEl.removeAttribute('title'); } }); } } } };
So, in the bind hook of the directive, we should have a mouseover
event listener on the element we had the v-tooltip on.
To check if the element has an ellipsis or not we can compare the offsetWidth
and scrollWidth
of the element. What is offsetWidth and scrollWidth?
offsetWidth is a measurement in pixels of the element’s CSS width, including any borders, padding, and vertical scrollbars.
The scrollWidth is a measurement of the width of an element’s content, including content not visible on the screen due to overflow
Inside the mouseover
event we will see if the width of the element is less than the content of the element. If it is, we will set the title tag to display the tooltip.
We also need to clear/remove the title tag when the content in the field is within the width of the element. Otherwise, we would end up displaying the previous title tag though the contents of the field are modified.
Usage
Let’s apply the directive we created to the element using v-tooltip
.
For this article, let’s create a <span>
tag with 50px
as width and let’s have the text such that the text will exceed 50px
and it shows an ellipsis.
<span class="ellipsis" v-tooltip>This is a very long text</span> <style> .ellipsis { width: 50px; overflow: hidden; text-overflow: ellipsis; display: inline-block; white-space: nowrap; } </style>
The above span tag will show ellipsis and when hovered on the text, we should see a tooltip displayed.
Here is the working demo in GIF.
Notice that I have the ellipsis styles in the style tag for this component. But, If we want to apply the ellipsis styles for many components this task will be repetitive. There are two ways to fix the ellipsis CSS from being duplicated.
- Use a separate class that should just display the ellipsis (without having the width of the element in the CSS otherwise you may end up having the same width for all the elements)
- Directive taking care of the ellipsis styles. (Next section)
Wrapping the ellipsis styles in the directive [OPTIONAL]
Instead of having the ellipsis CSS class tied to the HTML, we can also have the ellipsis CSS created dynamically when the HTML tag has v-tooltip
directive.
Going back to our v-tooltip
directive, we will just add style properties to the element. So, our bind
function will look like this after adding ellipsis styles.
bind: function (el) { el.style.textOverflow = "ellipsis"; el.style.display = "inline-block"; el.style.whiteSpace = "nowrap"; el.style.overflow = "hidden"; el.addEventListener('mouseover', function (evt) { let targetEl = evt.target; if (targetEl.offsetWidth < targetEl.scrollWidth) { targetEl.setAttribute('title', evt.target.textContent); } else { targetEl.hasAttribute('title') && targetEl.removeAttribute('title'); } }); }
This is optional. If you don’t want to have the styles wrapped in the tooltip directive, you can ignore adding them in the directive.
This should still work. Here is the demo again with CSS built into the bind function.
Here is the complete demo of v-tooltip
in codesandbox.
Karthik is a passionate Full Stack developer working primarily on .NET Core, microservices, distributed systems, VUE and JavaScript. He also loves NBA basketball so you might find some NBA examples in his posts and he owns this blog.
Pingback: Dew Drop – June 22, 2020 (#3218) | Morning Dew