How to create Better Tooltips with plain JavaScript and CSS

Today we’ll be building tooltips with JavaScript. Tooltips are the small snippet of text that shows when you hover over a link. The text is from the “Title” attribute of the anchor tag – which is supposed to describe to the user what the link is about, before they click it.
View Demo
The default browser tooltip is already in place. However, it doesn’t look so good. So let’s go ahead and create our own.
Below is how our tooltips will look once we’re finished:
js-tooltip
You may be thinking, why create something that has been done by many in the past? Why not just use a plugin like jQuery UI? Why reinvent the wheel?
I always feel that developers write code to hone their skill. The more you write, the better you get. There may be hundreds of plugins that take care of this functionality – and most likely they are going to be better than what we build.
But that’s what’s important here. To me, it’s the act of building and writing code. Knowing what’s going on under the hood. Gaining the knowledge it takes to understand how things work.
I guess my point is, we’re building our own tooltip – to learn how. That’s all.
So ready to get started? Note that we’re not using jQuery – just plain JavaScript. Doing it this way will expose us to the inner workings of the raw script.
Let’s begin.

Get all the links

First of all, we need to get all the links in the page. We have a simple utility called “document.links” – which returns an array of links! What a way to get started! Let’s go ahead and create a wrapper for our code.

(function(){
  //all code goes in here
})()

The above is also known as an “Immediately Invoked Function Expression“. This code is immediately run as soon as the page is loaded. It’s similar to jQuery’s “document.ready()” handler.
Now it’s time to grab all the links in the page. Notice that we’re simply doing a “for” loop, and checking if the link has a “title” attribute.

var links = document.links;
  for(var i=0; i < links.length; i++){
     var a = links[i];
     if(a.title !== ''){
       a.addEventListener('mouseover',createTip);
       a.addEventListener('mouseout',cancelTip);
     }
    //  console.log(a);
  }

We then bind two functions to our links using “addEventListener()”. One during “mouseover” and “mouseout”. The functions are named “createTip” and “cancelTip” (which we haven’t created yet). Know that “addEventListener()” is similar to jQuery’s “.on()”.
It’s funny that I’m writing a tutorial on pure JavaScript and comparing functions to jQuery. Usually, it’s the other way around.
Moving on…

Remove the default browser tip

Before we create the tooltip, we need to get rid of the default browser behavior of showing the ugly tip:
tooltip1
The trick is to remove the “title” attribute, and putting it in a temporary location (like another attribute). This way we can retrieve it for later use (like for our own tooltip and putting the title back).
Let’s create our first function that happens on “mouseover”. Then let’s add the code that strips out the title and adding it to a new attribute called “tooltip”:

function createTip(ev){
    var title = this.title;
    this.title = '';
    this.setAttribute("tooltip", title);
}

Let’s create our second function that happens during “mouseout”. This time, setting the attributes to its original state:

function cancelTip(ev){
    var title = this.getAttribute("tooltip");
    this.title = title;
    this.removeAttribute("tooltip");
}

Now, when you hover over the link, you will see our attributes are being manipulated by our function:
js-tooltip2
Let’s continue building the tooltip.

Create the new Tip

Now that we got that out of the way, and we have both functions in place, it’s time to build our tooltip. In summary, we need to create a div, add our text inside and put it inside the body of the HTML.
Add the code below inside our already existing createTip() function – just underneath the setting attribute code:

    var tooltipWrap = document.createElement("div"); //creates div
    tooltipWrap.className = 'tooltip'; //adds class
    tooltipWrap.appendChild(document.createTextNode(title)); //add the text node to the newly created div.
    var firstChild = document.body.firstChild;//gets the first elem after body
    firstChild.parentNode.insertBefore(tooltipWrap, firstChild); //adds tt before elem

Notice the new DIV we’ve created and added our class “tooltip”. This allows us to style our tip – which is why we’re doing this in the first place. We then append our text inside the div using .createTextNode() and .appendChild().
Finally, we insert the new DIV right underneath the BODY tag. We do this by finding the first child of the BODY element, then we do a “insertBefore()” it with our new DIV.
But wait, our DIV is in place – but it’s all the way at the top! We continue below by positioning our tip.

Positioning

Now this is probably the trickiest part of the code. It also depends on how you want the tooltip to appear, so you must adjust accordingly. In my case, I want the default position of the tip to be right above the link I’m hovering. Plus the tips have a little arrow pointing down – so we need to make room for that as well.
First, let’s plugin some styles so we get a better idea of how much to adjust. I wanted my tooltip to have a darker background, with a slight orange font color.

  .tooltip {
    position: absolute;
    background: #646464;
    border-radius:4px;
    padding: 6px 12px;
    font-family: arial;
    font-size: 12px;
    text-shadow: 0px 1px 1px #000;
    color: #ffc64a;
  }
  .tooltip:before {
    content : " ";
    width: 0;
    height: 0;
    border-left: 10px solid transparent;
    border-right: 10px solid transparent;
    border-top: 10px solid #646464;
    position:absolute;
    bottom:-5px;
    left:5px;
  }

You will also notice a :before pseudo class. This is to create the “arrow” that’s pointing down. Now that we have our styles, we know how much to adjust.
Next we get the coordinates and dimensions of the link and the tip itself. We do this by doing a “getBoundingClientRect()”. Once you do a console.log, you should see an objects that has the tooltip and link properties that we need:
tooltip2
The top and left properties is the X and Y coordinates (or where it is in the window), plus the height and width. We need the height to calculate and offset our tip.
Let’s add our new code below inside the createTip():

   var padding = 5;
   var linkProps = this.getBoundingClientRect();
   var tooltipProps = tooltipWrap.getBoundingClientRect();
   var topPos = linkProps.top - (tooltipProps.height + padding);
   tooltipWrap.setAttribute('style','top:'+topPos+'px;'+'left:'+linkProps.left+'px;')

As I was saying, we simply calculate the position of our tooltip by offsetting based on where the link is. We also add some padding and take in account the height of the tip. We add this new position to the tip by adding an inline “style” with “top” and “left”.
Now hover over a link and you should see our newly styled tip, positioned nicely above the link.
tooltip3
We can also simply remove all tips from the page by adding the code below to our “cancelTip()” function:

   document.querySelector(".tooltip").remove();

And that’s basically it.

Conclusion

This is probably the simplest tooltip we can build. There are plenty of things to consider – such as image links, or positioning the tips below or to the left or right. These things we can add as an enhancement in the our next session. I will also build a Git repository and a demo in my next tutorial – so stay tuned.

17 Comments

  1. I like tooltips as it is made by java script and css and java script is my favorite language .
    Thanks for sharing here such nice tools system here . Hopefully more new collections will be continued through regular sharing in this site .

    Reply
  2. If the page has been scrolled, the tooltip shows up offset from the link. In other words, the tooltip seems to be anchored to the links original position. This is true in Safari, Firefox, and Chrome. Don’t know about windows only browsers. You can see for yourself here: https://codepen.io/alihesari/pen/jagGOr Just make your window small enough that the text at the bottom can be scrolled and watch what happens with the tooltip.

    Reply
  3. Hi,
    The tooltip’s position seems to be off. I came to this article looking for a way to fix the scroll issue for tooltips. Do you have a way you can recommend to fix the position of the tooltip popup with the element it is showed up on. Right now, if you make the browser small enough to have scrollbars, tooltips seemed fixed to their DOM position rather than the highlighted text’s position. Also is their a way to update the position based on browser’s view port?

    Reply
  4. This only works so long as your link is far enough down to display the tooltip. What would be nice is to explain how to position the tooltip so that it is always on the screen. (i.e. under the link if it is at the top of the page).
    Also, binding the events to the links themselves disallows new items to be added and also have their links created. Establishing a delegate for all tooltip events would likely be a better solution as it would allow future links with titles to also be handled by the improved tooltip appearance.
    Thanks for the tutorial.

    Reply
    • Hi,
      i am looking for ways to reposition the tooltip automatically. curious if you have found any? if on the right of the screen throw tip to the left, if on a small screen, make the tip more vertical, etc. as i type this, it seems css frameworks are the best way to do so, yet would still like to be able to detect the side of the screen on a desktop so the tool tip could be resized etc.

      Reply
  5. OT: Delete previous messages. This is the final post, it didn’t show correctly the data-title inside the HTML a tag (replaced it with another attribute). Sorry for the tests.
    Instead of creating a div with the tooltip class, you could replace it with a pseudo-tooltip made in CSS and a custom attribute.
    Save title value in data-title attribute without creating class or div.
    Example:
    HTML

    <a href="example.com" data-title="Lorem Ipsum"> Link </a>

    CSS

    [data-title]: after {
      content: attr (data-title);
      position: absolute;
      bottom: -1.6em;
      left: 100%;
      background-color: black;
      color: white;
    }
    

    Could you try it?
    Greetings from Argentina and sorry for my English.

    Reply

Leave a Reply to Gregory Lucy Cancel reply