Science · Code · Curiosity
Tooltip
Introduction
[May 2, 2018: I've updated this tutorial to fix issues with the viewBox and generally make the code neater.]
The previous tutorial covered basic mouseover effects. This one explains how to use javascript to create a more complex effect: showing a text element when the mouse is hovered over other SVG elements. The code for all the examples on this page can be found here.
The title element
Since writing this tutorial, the title tag has become well supported. However, this tutorial should still be useful if you want a specific style of tooltip. It can also be adapted to create other mouseover effects since it covers several of techniques and principles for making interactive SVG elements. The title tag is also a little annoying to add since you have to create a new element inside the existing one, rather than adding an attribute.
To use the title tag, add it inside the elements like so:
<rect x="40" y="50" width="80" height="100" fill="#007bbf">
<title>A blue box</title>
</rect>
<rect x="180" y="50" width="80" height="100" fill="#ec008c">
<title>A pink box</title>
</rect>
Then, when you hold your mouse over each element, a tooltip appears after a second or so.
Coding our own
If that's all you want, then the title tag is fine. But what if we wanted to make our own, maybe making the background was a different shape.
The first thing to do is to add a <text> element at the end of your SVG so it will be drawn over the top of the other elements. It doesn't matter what the x and y attributes are, since we will dynamically change them later, and it is current hidden due to the visibility="hidden" attribute. I've given the text element the id "tooltip" which allows us to easily select it.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200">
<rect class="tooltip-trigger" x="40" y="50" width="80" height="100" fill="#007bbf" />
<rect class="tooltip-trigger" x="180" y="50" width="80" height="100" fill="#ec008c" />
<text id="tooltip" x="10" y="190" visibility="hidden">Tooltip</text>
</svg>
Also add a class to the elements you want to trigger a tooltip. I used "tooltip-trigger" as a class name, but you can use whatever you like, it's just there so we can easily select the elements.
Selecting the tooltip
For this tutorial, I'm going to write the code in a <script> element inside the SVG itself (which means it will still work if I email the SVG to someone). Make sure the script element appears as the last element in the SVG, so the tooltip <text> element exists when the script is run. The code will be the same if you put the JS in a separate file, so long as the SVG is inline, otherwise see my SVG + JS tutorial for how to access the SVG document.
First we need to get the tooltip <text> element. I'm doing this inside an immediately invoked function expression (IIFE). The advantage of this is that it keeps the variables and methods private, so I can have multiple versions running on this page without them interfering with each other (e.g. the tooltip variable won't get overwritten by each script element). It not necessary if you have a single JS file outside of each SVG or have external SVGs embedded into the page, but it's still good practise.
<script type="text/javascript"><![CDATA[
(function() {
var tooltip = document.getElementById('tooltip');
})();
]]></script>
Displaying the tooltip
Next we get the trigger elements with getElementsByClassName and loop through them, adding event handlers for mousemove and mouseout. I've used mousemove rather than mouseover so when we change the position of the tooltip later, it will be constantly updated. If you use mouseover the tooltip to remain stationary after it first appears, which is what happens when you use the <title> element.
var triggers = document.getElementsByClassName('tooltip-trigger');
for (var i = 0; i < triggers.length; i++) {
triggers[i].addEventListener('mousemove', showTooltip);
triggers[i].addEventListener('mouseout', hideTooltip);
}
The event handlers added referred to two functions: showTooltip and hideTooltip, which we need to write. These make the tooltip visible or hidden respectively.
function showTooltip(evt) {
tooltip.setAttributeNS(null, "visibility", "visible");
}
function hideTooltip() {
tooltip.setAttributeNS(null, "visibility", "hidden");
}
Now if we move the mouse over either box, the tooltip appears at the bottom of the SVG. When we move the mouse off either box, the tooltip disappears.
Positioning the tooltip
Now we want the tooltip to appear next to the mouse. In order for the positioning to work we need to understand the effect of the SVGs viewBox attribute. I've explained in detail how this works here. First we need to get the SVG element which we can do by giving it an id, then using getElementById.
var svg = document.getElementById('tooltip-svg');
Then we update the showTooltip function to get the screen current transform matrix and use this to calculate where the tooltip should be. Again, I explain what getScreenCTM() returns and how to use it here.
function showTooltip(evt) {
var CTM = svg.getScreenCTM();
var mouseX = (evt.clientX - CTM.e) / CTM.a;
var mouseY = (evt.clientY - CTM.f) / CTM.d;
tooltip.setAttributeNS(null, "x", mouseX + 6 / CTM.a);
tooltip.setAttributeNS(null, "y", mouseY + 20 / CTM.d);
tooltip.setAttributeNS(null, "visibility", "visible");
}
I've shifted the position of tooltip by 6 units in the x direction and 20 units in the y direction because I thought that worked well, but you can play around with the numbers. I also added the style dominant-baseline="hanging" to the tooltip text element so the distance is measured from its top. This means there should be a constant distance between the mouse cursor and the tooltip text regardless of the dimensions of the SVG.
Notice that if you move the mouse to the right of the rightmost box, the tooltip is cut off. You can add overflow="visible" to the SVG style to avoid this.
Changing the tooltip text
It's not very useful to display the same text no matter which element is mouseovered. We can change the text by changing firstChild.data for the tooltip element. For example, we can change the mouseover text to display the colour of the square:
tooltip.firstChild.data = evt.target.getAttributeNS(null, "fill");
This method works when you want the mouseover text to display an attribute of elements, but normally this won't be the case. However, we can always add an attribute that contains the text we want to display. Generally when you add attributes contain information we want to use, the attribute should start with data-. For example, I updated the boxes to have data-tooltip-text attributes.
<rect x="40" y="50" width="80" height="100" fill="#007bbf" class="tooltip-trigger" data-tooltip-text="Left box"/>
<rect x="180" y="50" width="80" height="100" fill="#ec008c" class="tooltip-trigger" data-tooltip-text="Right box"/>
Then you can use:
tooltip.firstChild.data = evt.target.getAttributeNS(null, "data-tooltip-text");
Adding the background
Since it can be hard to read mouseover text on dark backgrounds, it makes sense to display the text in its own box. We can make the tooltip more complex by converting it into a <g> element and then adding child elements, such as a <rect> element for a shadow, a <rect> element for a box, and then the tooltip <text> element.
<g id="tooltip" visibility="hidden" >
<rect x="2" y="2" width="80" height="24" fill="black" opacity="0.4" rx="2" ry="2"/>
<rect width="80" height="24" fill="white" rx="2" ry="2"/>
<text x="4" y="6">Tooltip</text>
</g>
The position of the group and the white <rect> element is (0, 0), and the other positions are relative to that. Group elements (<g>) don't have x and y coordinates (or at least they don't change their children's position), so instead we add a translate transform.
var x = (evt.clientX - CTM.e + 6) / CTM.a;
var y = (evt.clientY - CTM.f + 20) / CTM.d;
tooltip.setAttributeNS(null, "transform", "translate(" + x + " " + y + ")");
(Note that I combined adding the (+6, +20) offsets with the calculation of the mouse position, just to make things a bit more efficient).
This way of adding transforms with setAttributeNS is slightly hacky, but it's fine so long are no other transforms are added to the tooltip element (since they will be overwritten). The "correct" method would be to use createSVGTransform() as demonstrated here.
We also need to update the way we change the content of the text element since the tooltip variable no longer directly refers to it. So we can create a new variable to refer to the text inside the first <text> element inside the tooltip group:
var tooltipText = tooltip.getElementsByTagName('text')[0];
Then we change the code that updates the text to use the new variable.
tooltipText.firstChild.data = evt.target.getAttributeNS(null, "data-tooltip-text");
Now our tooltip has a shadow and a box. However, the box and shadow have a fixed width, so we need to make sure it's bigger than the longest text. But then that can look odd with shorter text.
Changing the tooltip length
In order to dynamically change the length of the tooltip boxes we first need to get the boxes, which we can do by getting all the <rect> elements inside the tooltip group.
var tooltipRects = tooltip.getElementsByTagName('rect');
Then, in the showTooltip function we find the length of the tooltip <text> element with getComputedTextLength(), and use this to set the length of the rects.
var length = tooltipText.getComputedTextLength();
for (var i = 0; i < tooltipRects.length; i++) {
tooltipRects[i].setAttributeNS(null, "width", length + 8);
}
Comments 44
Leave a comment
Comments are moderated and will appear after approval.
Thank you, thank you, thank you. Saved me a couple of hours finding all of the info I needed to do this. I wish the Firefox SVG renderer would just show title elements as tooltips. Goodness, how many years has it been?
Thanks, I'm glad you found it helpful.
Dang! This is slick and was just what I needed for an SVG-based visualization I was working on.
(I'm glad I procrastinated on the project or I would have had to figure it out myself. :)
Hello Peter,
Thanks for all tutorials, really help me to work with svg. I would like to ask you about svg and android tablets. I know that only works on tablets that have honeycomb, right? is any difference with ipad? Do you know any website where find more information about svg and tablets?
Thanks
Viviana
Hi Viviana, I'm afraid I don't know anything about getting SVG to work on tablets, sorry.
Note that if you are working with large SVG, lines 25 and 26 fail when the user scrolls the SVG/page. The tooltips would appear at the wrong place, depending how much the user scrolls even outside the current view.
Easiest way is to just use "evt.pageX" and "evt.pageY" instead of clientX/clientY which take into account the scrolling but these may not be cross-browser compatible. There are sites that explain how to do this correctly.
Hi,
I'm having the problems that Ville Ojamo says, my tooltip is at the wrong place, I play around changing the values of clientX and clientY but the position is always the same, and far away from the cursor. I've tried changing also client by page, but nothing change.
Could someone help me with this please?
Thanks a lot
Another Great tutorial!! Many thanks
I sorted it out, I just put negative value on pageX and it worked.
Many thanks for this blog so helpful
Glad you managed to fix your problem Ichi. Ville is right about using pageX and pageY - I've updated the page.
Hi,
I am new in SVG, these tutorials are very helpful.
My problem is that when I am adding viewbox to svg i am loosing the tooltip:(
Your tutorial is just GREAT - Thanks a lot!
Hi there is there a way to multipul tooptip boxes that have different colour stokes?
i am doing a task regarding SVG map and your tutorial seriously help me a lot.
thank you very much!
but i have a question, how can we change the tooltip information with image?
for example, it is a world map and when user mouse over at a country then a tooltip with the country's map will shown
Hi Amy,
What you'd have to do would be to draw the images off screen, and when the mouse goes over an area, select the right image and move its display in the same way the tooltip background is moved into place.
Great! That's exactly what I was looking for.
Thank You!
Hi,
Its great and I found what I want but can u tell me how dynamic data show on a mouse move
i have a problem ; when i point in my map.svg the viewbox far away from the cursor ;and i don't understand what does 't mean Ichi. please give me a script or any exemple .
Hi
This guide is excellent but I need the tooltip data from a php scripts
Thank
I've found your tutorials to be really useful - especially now that I'm branching out into animated SVG. I'd never created or modified the shapes of elements using javascript before, and it's proved an invaluable technique for animating gauges and the like. Keep up the good work!
I am following your tutorial. Its really helpful for my recent development. But I m having a serious issue and not able to find any solution. Can you please check this question -
https://stackoverflow.com/questions/24187613/external-css-file-doesnt-have-the-effect-on-external-svg-file-in-html-page
How can I do this.
Would be grateful if you kindly reply soon.
Thanks
Hi Peter, A very nice tutorial, thanks for sharing!
I am trying to adapt this to show a tooltip for a marker on a polyline element. I have a graph drawn by using polyline elements to plot the data, and I would like to have a hover text on each marker "dot" to show the value at that point on the graph.
Is this possible?
Cheers,
Greg.
Thanks for the very useful tutorial. I've adopted the code in my project and it works fine. My only problem is that I need to use viewBox attribute, when I added it in the svg tag, it will move the tooltip far away, sometimes invisiable anymore. Take the example of your tooltip_final.svg:
When the viewBox attribute is added, it mess up the tooltip. I feel that this has something to do with the coordination system, but I don't know how to fix it. Can you help me?Thanks
Very useful, thanks !
Is there a way to show multiple line tooltip?
Thanks
Your tutorials and examples have been tremendously helpful!
One more thing though...how can I display multiple lines on a tooltip?
Thanks alot for the anticipated response! :)
First of all, thanks for all of this information, it has been great and I've been feeling really inspired to make some cool graphics with data that I have.
Is it possible to apply the tooltip functions to classes without having to add the lines of code for each one? Similar to the way you suggested for the '.some-class-name:hover' method in the
Hi everybody,
For some reason, Dreamweaver CC is indicating that I have an coding error on the line that reads <![CDATA[. the first line of code after the the
HORRIBLE tutorial. There needs to be a full code somewhere. I had no idea where to put cdata and kept getting errors.
No.
Hi Peter,
this is really helpful and step by step learning for a beginners in SVG's. very good topic , very god effort. Thank you so much again.
Best Regards,
Hi Peter,
this is realy good effort to help beginners with SVG issues.
Best Regards,
Very nice Tutorial! Thank you very much!
I have just one little issue... How can I create a linebreak in the Tooltip-Text? (would like to use something like '\n' - but that doesn't work...
Has anyone a hint ?
Thanks
Thanks, very useful!!!
Hi Peter,
Great Writing!
Thanks a lot!
Hi Peter, thank you for your tutorial. It's very useful. However, I really need to have multiple lines in my tooltip, but I can't get this working. Do you have any tips?
Thank you very much!
I realize this post is a bunch of years old by now. But I'm trying to implement it and having some issues. Perhaps syntax should be different now? In any case, I'm trying to add a tooltip to a path. It sort of works. I can see that there's a tooltip around, but it sits a long ways away from the element itself. How can I make sure the tooltip stays near the mouse itself?
Hello Peter,
Is it possible to have html tooltip for SVG?
Thanks
Hi. How to fix the tootltip position? My svg map is responsive but when i look on my tablet the position of the toolbar is changed.
Hi Peter,
I've been trying to add images to the tooltips, but failed again and again.
If it is in fact possible to do so, I was hoping if you could lend your expertise on the matter.
Thanks in advance.
Daniel
I know this is a necropost, but this is still the only way to create an SVG tooltip that you can actually format. Thanks for keeping it online!
Hi there,
I have been searching and searching and I cannot find out how to include a new line/break within the tooltip. Do you know how I possibly could do that?
And thank you for the tutorial, very useful!
Thanks Peter! Your tutorials have made my task easier.
excellent work Peter, almost 9 years later, still helps a lot.
little error in the code, should be corrected.
triggers[i].addEventListener('mousemove', ShowTooltip);
triggers[i].addEventListener('mouseout', HideTooltip);
Thanks, I've update the text.
Hi Peter,
Thanks for these tutorials they help me a lot. As I want to display a real tree with almost 100 leafs on a joomla site I want to refrain as much as possible from external libraries like javascript and jquery.
I can't seem to get text ( 2 to 3 lines) belonging to leaf1 displayed as I hover over leaf1, leaf2 should display another text on hovered over. Ideally this text would come over the trunk.
I have the hovering and clicking working but the text eludes me for some reason (https://codepen.io/Forssux/pen/vYENyMX?editors=1111) thanks for reading anyway and your posts.
This is exactly what I was looking for, solid work!