v-tooltip: A custom directive for showing tooltip in VUE JS

v-ttoltip featured image coderethinked.com

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.

ellipsis demo with styles in css
ellipsis demo with styles in CSS

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.

  1. 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)
  2. 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.

ellipsis without styles in css
ellipsis without styles in css

Here is the complete demo of v-tooltip in codesandbox.