The following post is a portion of the D3 Tips and Tricks document which it free to download. To use this post in context, consider it with the others in the blog or just download the pdf and / or the examples from the downloads page:-)
--------------------------------------------------------
Tooltips have a marvelous duality. They are on one hand a pretty darned useful thing that aids in giving context and information where required and on the other hand, if done with a bit of care, they can look very stylish :-).
Technically, they represent a slight move from what we have been playing with so far into a mildly more complex arena of 'transitions' and 'events'. You can take this one of two ways. Either accept that it just works and implement it as shown, or you will know what s going on and feel free to deride my efforts as those of a rank amateur :-).
The source for the implementation was taken from Mike Bostock's example here; http://bl.ocks.org/1087001. This was combined with a few other bit's and pieces (the trickiest being working out how to format the displayed date correctly and inserting a line break in the tooltip (which I found here; https://groups.google.com/forum/?fromgroups=#!topic/d3-js/GgFTf24ltjc (well done to all those participating in that discussion)). I make the assumption that any or all errors that occur in the implementation will be mine, whereas, any successes will be down to the original contributors.
So, just in case there is some degree of confusion, a tooltip (one word or two?) is a discrete piece of information that will pop into view when the mouse is hovered over somewhere specific. Most of us have seen and used them, but I suppose we all tend to call them different things such as 'infotip', 'hint' or 'hover box' I don't know if there's a right name for them, but here's an example of what we're trying to achieve;
You can see the mouse has hovered over one of the scatter plot circles and a tip has appeared that provides the user with the exact date and value for that point.
Now, you may also notice that there's a certain degree of 'fancy' here as the information is bound by a rectangular shape whit rounded corners and a slight opacity. The other piece of 'fancy' which you don't see in a pdf is that when these tool tips appear and disappear, they do so in an elegant fade-in, fade-out way. Purty.
Now, before we get started describing how the code goes together, let's take a quick look at the two technique specifics that I mentioned earlier, 'transitions' and 'events'.
Transitions
From the main d3.js web page (d3js.org) transitions are described as gradually interpolating styles and attributes over time. So what I take that to mean is that if you want to change an object, you can do so be simply specifying the attribute / style end point that you want it to end up with and the time you want it to take and go!
Of course, it's not quite that simple, but luckily, smarter people than I have done some fantastic work describing different aspects of transitions so please see the following for a more complete description of the topic;
- Mike Bostock's Bar chart tutorial (http://mbostock.github.com/d3/tutorial/bar-2.html)
- Christophe Viau's 'Try D3 Now!' tutorial (http://christopheviau.com/d3_tutorial/)
Hopefully observing the mouseover and mouseout transitions in the tooltips example will whet your appetite for more!
Events
The other technique is related to mouse 'events'. This describes the browser watching for when 'something' happens with the mouse on the screen and when it does, it takes a specified action. A (probably non-comprehensive) list of the types of events are the following;
- mousedown: Triggered by an element when a mouse button is pressed down over it
- mouseup: Triggered by an element when a mouse button is released over it
- mouseover: Triggered by an element when the mouse comes over it
- mouseout: Triggered by an element when the mouse goes out of it
- mousemove: Triggered by an element on every mouse move over it.
- click: Triggered by a mouse click: mousedown and then mouseup over an element
- contextmenu: Triggered by a right-button mouse click over an element.
- dblclick: Triggered by two clicks within a short time over an element
How many of these are valid to use within d3 I'm not sure, but I'm willing to bet that there are probably more than those here as well. Please go to http://javascript.info/tutorial/mouse-events for a far better description of the topic if required.
Get tipping
So, bolstered with a couple of new concepts to consider, let's see how they are enacted in practice.
If we start with our simple-scatter plot graph there are 4 areas in it that we will want to modify (it may be easier to check the tooltips.html file in the example files in the downloads section on d3noob.org).
The first area is the CSS. The following code should be added just before the </style> tag;
div.tooltip { position: absolute; text-align: center; width: 60px; height: 28px; padding: 2px; font: 12px sans-serif; background: lightsteelblue; border: 0px; border-radius: 8px; pointer-events: none; }These styles are defining how our tooltip will appear . Most of them are fairly straight forward. The position of the tooltip is done in absolute measurements, not relative. The text is centre aligned, the height, width and colour of the rectangle is 28px, 60px and lightsteelblue respectively. The 'padding' is an interesting feature that provides a neat way to grow a shape by a fixed amount from a specified size.
We set the boarder to 0px so that it doesn't show up and a neat style (attribute?) called border-radius provides the nice rounded corners on the rectangle.
Lastly, but by no means least, the 'pointer-events: none' line is in place to instruct the mouse event to go "through" the element and target whatever is "underneath" that element instead (Read more here; https://developer.mozilla.org/en-US/docs/CSS/pointer-events). That means that even if the tooltip partly obscures the circle, the code will stll act as if the mouse is over only the circle.
The second addition is a simple one-liner that should (for forms sake) be placed under the 'parseData' variable declaration;
var formatTime = d3.time.format("%e %B");This line formats the date when it appears in our tooltip. Without it, the time would default to a disturbingly long combination of temporal details. In the case here we have declared that we want to see the day of the month (%e) and the full month name(%B).
The third block of code is the function declaration for 'div'.
var div = d3.select("body").append("div") .attr("class", "tooltip") .style("opacity", 0);We can place that just after the 'valueline' definition in the JavaScript. Again there's not too much here that's surprising. We tell it to attach 'div' to the body element, we set the class to the tooltip class (from the CSS) and we set the opacity to zero. It might sound strange to have the opacity set to zero, but remember, that's the natural state of a tooltip. It will live unseen until it's moment of revelation arrives and it pops up!
The final block of code is slightly more complex and could be described as a mutant version of the neat little bit of code that we used to do the drawing of the dots for the scatter plot. That's because the tooltips are all about the scatter plot circles. Without a circle to 'mouseover', the tooltip never appears :-).
So here's the code that includes the scatter plot drawing (it's included since it's pretty much integral);
svg.selectAll("dot") .data(data) .enter().append("circle") .attr("r", 5) .attr("cx", function(d) { return x(d.date); }) .attr("cy", function(d) { return y(d.close); }) .on("mouseover", function(d) { div.transition() .duration(200) .style("opacity", .9); div .html(formatTime(d.date) + "<br/>" + d.close) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY - 28) + "px"); }) .on("mouseout", function(d) { div.transition() .duration(500) .style("opacity", 0); });
Before we start going through the code, the
example file for tooltips that is on d3noob.org includes a brief
series of comments for the lines that are added or changed from the
scatter plot, so if you want to compare what is going on in context,
that is an option.
The first six lines of the code are a repeat of
the scatter plot drawing script. The only changes are that we've
increased the radius of the circle from 3.5 to 5 (just to make it
easier to mouse over the object) and we've removed the semicolon from
the 'cy' attribute line since the code now has to carry on.
So the additions are broken into to areas that
correspond to the two events. 'mouseover' and 'mouseout'. When the
mouse moves over any of the circles in the scatter plot, the
mouseover code is executed on the 'div' element. When the mouse is
moved off the circle a different set of instructions are executed.
It would be a mistake to think of tooltips in the
plural because there aren't a whole series of individual tooltips
just waiting to appear for their specific circle. There is only one
tooltip that will appear when the mouse moves over a circle. And
depending on what circle it's over, the properties of the tooltip
will alter slightly (in terms of its position and contents).
on.mouseover
The .on("mouseover"
line initiates the introduction of the tooltip. Then we declare the
element we will be introducing ('div') and that we will be applying a
transition to it's introduction (.transition()).
The next two lines describe the transition. It will take 200
milliseconds (.duration(200))
and will result in changing the elements opacity to .9
(.style("opacity",
.9);).
Given that the natural state of our tooltip is an opacity of 0, this
make sense for something appearing, but it doesn't go all the way to
a solid object and it retains a slight transparency just to make it
look less permanent.
The following three lines format our tooltip. The
first one adds an html element that contains our x and y information
(the date and the d.close value). Now this is done in a slightly
strange way. Other tooltips that I have seen have used a '.text'
element instead of a '.html' one, but I have used '.html' in this
case because I wanted to include the line break tag “<br/>”
to separate the date and value. I'm sure there are other ways to do
it, but this worked for me. The other interesting part of this line
is that this is where we call our time formatting function that we
described earlier. The next two lines position the tooltip on the
screen and to do this they grab the x and y coordinates of the mouse
when the event takes place (with the d3.event.pageX and
d3.event.pageY snippets) and apply a correction in the case of the y
coordinate to raise the tooltip up by the same amount as its height
(28 pixels).
on.mouseout
The .on("mouseout"
section is slightly simpler in that it doesn't have to do any fancy
text / html / coordinate stuff. All it has to do is to fade out the
'div' element. And that is done by simply reversing the opacity back
to 0 and setting the duration for the transition to 500 milliseconds
(being slightly longer than the fade-in makes it look slightly cooler
IMHO).
Right, there you go. As a description it's ended
up being a bit of a wall of text I'm afraid. But hopefully between
the explanation and the example code you will get the idea. Please
take the time to fiddle with the settings described here to find the
ones that work for you and in the process you will reinforce some of
the principles that help D3 do it's thing.
I've placed a copy of the file for drawing the
tooltips into the downloads section on d3noob.org with the general
examples as tooltips.html.
Edit: 2014-05-02: After an interesting question below I have added a new post on including an HTML link in out tooltips and it can be found here.
Edit: 2014-05-02: After an interesting question below I have added a new post on including an HTML link in out tooltips and it can be found here.
The above description (and heaps of other stuff) is in the D3 Tips and Tricks document that can be accessed from the downloads page of d3noob.org.
D3Noob,
ReplyDeleteFollowed your examples from start to finish and found them really useful. As a complete novice at D3 I've come a long way over the last two days and started working with my own data. I've currently two lines on the graph displaying properly and am now looking at setting up a transition that will allow for each line to be brought in on clicking a button. Any examples of how this would work?
appreciate you effort in getting all this on line
Justin
Great stuff Justin.
DeleteExcellent to hear that the information is useful.
I don't have an example for the basic code that you could follow, but if it's any solace, it's on my (long and getting longer) list of things to add to the tips and tricks manual.
Mike Dewar has a really good example in his book 'Getting started with D3' that uses multiple lines and you toggle a legend to switch them on and off. But for my money I would start with an excellent post by Jerome Cukier on transitions here; http://blog.visual.ly/creating-animations-and-transitions-with-d3-js/ one of his examples looks like exactly what you describe and I will be taking a long hard look at it when it comes time to add this to the manual.
When you get it sorted, if you think it would be a useful addition to the tips and tricks manual I would be really keen to add it in (if you're happy to release the code :-))
Thanks for reading.
Or maybe even this post by Ivan Teoh http://www.ivanteoh.com/posts/176-d3-tutorial-scale-graph.html
DeleteI am lazy. I prefer to simply incorporate jquery ui and append a title. It also allows me to embed HTML in the tooltip. No mouse over hassles either. Of course, it adds new dependencies to your visual, but I am good with the tradeoff.
ReplyDelete- Pat
Fair call, using jQuery opens up a whole world of other possibilities, so a good move in my opinion.
DeleteHi, great tutorial. Is it possible to embed a hyperlink in the tooltip? When I try it, it shows up formatted as a link, but clicking does nothing.
ReplyDelete-Sam
Wow! That's a great question! I think so if it was wrapped in the correct tags. I'll have a play and report back.
DeleteThis comment has been removed by the author.
DeleteThe answer is Yes! http://www.w3schools.com/html/html_links.asp was my friend.
DeleteSadly trying to put the code into the comments here confuses the blog and it tries to link to the url instead of displaying it :-(. But just add a sample link per the w3schools page above to the line that adds the text for your hyperlink. The only semi-tricky thing I had to do was to put the link (with tags) in single speech marks instead of double speech marks since the URL needed to be in double speech marks.
The only real problem then being that in order to reach it you need to move the mouse so far that the tooltip vanishes! (Of course this can be solved by several means depending on what's appropriate for the tooltip).
So yes. Great question and an actual answer!
Snap! I got all excited and didn't read your post properly! Now that I have I see that I am experiencing the same problem that you are! I think that there is a solution but it involves blocking mouse events. So I'm wondering if we might be in a chicken / egg situation where we can do one thing, but that prevents us from doing another. Hmm....
DeleteThanks for the quick response. I tried that, and I put a delay in the mouseout transition, so that I can actually move the mouse over and click on the tooltip before it vanishes. The text shows up formatted as a hyperlink in the tooltip, but clicking on it doesn't achieve anything (the target page does not open). Am I missing something?
Delete-Sam
Yeah. I think we're both missing something. I get the feeling that by utilising a mouse event as a trigger, we then bind the mouse action to the tooltip, so that while the hyperlink shows up, the mouse is still bound to the originating object and ignores the link. My experience in this area is sadly lacking I'm afraid. That's my best guess. If it's a 'must have' for your visualization, ask a question on stack exchange. There's a bunch of people in that forum with some rare skills. Sorry I wasn't able to help more.
DeleteNo worries. Thanks for your prompt replies. If I figure something out, I'll post back here as well.
Delete-Sam
Oops! Got it: there is "pointer-events: none" in your div.tooltip css. I took that out and it worked :-)
Delete-Sam
PS adding a 'target="_blank"' is a good idea so that the link opens in a new tab.
Legend! Nice job.
Deletecan you please give me an example for hyperlink in tooltip
DeleteGood question. The secret is to wrap the part of the text that you want to link with `a` tags (which by the sound of it you've done already) and then to remove the `pointer-events: none` attribute from the `style` section (otherwise the mouse will ignore the presence of the tool tip). I have generated an example on bl.ocks.org for your perusal (http://bl.ocks.org/d3noob/c37cb8e630aaef7df30d).
DeleteAgain, good question :-).
Actually, I also changed the way that the tool tip appears and disappears so that it gets more of a chance to stay on the screen when you move the mouse off the dot on the graph.
DeleteThank you .... its works
DeleteI need the link to be opened in the new tab ..can you tell me how??
div .html('' + // The first tag
Deleted.imdb +
"" + // closing tag
"
" + d.close)
this my code
am just trying to retrive the url from the data set....
can you tell me how to do it
To open in a new tab include `target="_blank"` in your opening `a` tag per this page http://www.w3schools.com/html/html_links.asp. And for retrieving your url from a data set you will want to edit the line that says `http://google.com` with `'+d.imdb+'` (make sure you pay attention to the quotes :-)
Deletewhen i gave a single hyperlink it works perfectly.but when i try to retrive the hyperlink from the data, it displays the hyperlink & on click it returns with error 404.
DeleteI think you'll need to post your code in a jsfiddle or on stack overflow to try and resolve it. The comments section here isn't conducive to the Q & A. Try jsfiddle or Stack Overflow and post the location of the code here.
DeleteOK, I admit it, I really like these questions :-). I have put together another example here (http://bl.ocks.org/d3noob/2e224baa7472c5e9e0e8) that has the links in the csv file and opens the links in another tab.
Deletethere was problem in my data, i just rectified it..it's works perfectly.
DeleteThank you.
i have add a update button...but i couldn't bind the data to the circle attributes.
Deletefunction updateData1() {
// Get the data again
d3.tsv("data/data2.tsv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0,450])
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Select the section we want to apply our changes to
var svg = d3.select("body").transition();
// Make the changes
svg.select(".line") // change the line
.duration(750)
.attr("d", valueline(data));
svg.select(".x.axis") // change the x axis
.duration(750)
.call(xAxis);
svg.select(".y.axis") // change the y axis
.duration(750)
.call(yAxis);
svg.selectAll("circle")
.transition()
.duration(500)
.attr("r", 2)
.attr("cx", function(d) { return x(d.date); }) .attr("cy", function(d) { return y(d.close); })
});
Hi Shiva, That's really a question that will need to go to Stack Overflow. It's just too messy to try and diagnose code examples in the comments section. Give Stack Overflow a whirl. It is really good for solving this sort of problem :-)
Deletehttp://stackoverflow.com/questions/23440532/how-to-update-tooltip
Deletehere is the link for my code please do have a look..
Thanks for the helpful post! But I was wondering where exactly did you bind the tooltip class to the mouseover event as you did not specifically refer to div.tooltip within the function. Am I missing something?
ReplyDeleteGood question. div.tooltip is obviously declared in the css. Then in the third block (above) it gets bound (at least that's where I believe it occurs) with the following;
Deletevar div = d3.select("body").append("div")
.attr("class", "tooltip")
Hey man, I've been working through alot of your tutorials and they're great. Better than some of the books I've seen on d3. Thanks man! I appreciate you giving away the fire.
ReplyDeleteGlad that they're useful!
DeleteJust spreading the D3 love :-).
Ya, thanks for giving away the fire!
ReplyDeleteNo problem! Interestingly I had never heard the phrase 'giving away the fire' before (other than the comment above yours). I shall try to use it in casual conversation!
DeleteWill it work on the Rickshaw javascript framework That uses D3?
ReplyDeleteWow! Good question. I hadn't even heard of Rickshaw. That looks really cool. The short answer is, I don't know, however, I do see that they have a interactive hover thingy that might do what you want if you want something simple.
DeleteI have been trying to adapt the tooltip code to a multi series line chart such as - http://bl.ocks.org/mbostock/3884955. No luck so far. I believe my problem is defining the cx and cy.
ReplyDeleteI think this would be do-able. Put your code and data into a question on Stack Overflow and any problems should be able to be worked out there. Good luck.
DeleteI have been trying to do the same thing. I can get the tooltip to render but it only appears at the bottom of the page (if bound with "body"). Were you able to make anything work?
DeleteNice tutorial. I have employed a slightly modified version to some baseball data here: http://visual-baseball.com/d3/examples/donut/AL_21_40_donuts.html
ReplyDeleteNice! Looks good.
DeleteHey thanks for the tutorial! How do you ensure your tooltip is generated on top of the svg element its appended too? When I try it the box appears under the svg
ReplyDeleteHow curious! Are you able to create a jsfiddle or bl.ocks.org example to demonstrate?
DeleteHey, Im trying to create a simple scatter plot with tooltip. My csv file is [country,x,y], I just want the country name (&x,y) to pop up to label each data point upon mouse over (there are ~200 clustered together around an origin so its a little untidy to lable). Simple problem, looking for simple solution?
ReplyDeleteHmm... Straight away my first thought is to massage the data before drawing the graph. It could be done dynamically using a php script that loaded the csv, massages and outputs as JSON (or csv again). Not entirely simple however (if you're not familiar with php).
ReplyDeleteI have the feeling that there will be a method available within d3's array functions (https://github.com/mbostock/d3/wiki/Arrays or this page may help http://bl.ocks.org/phoebebright/raw/3176159/) but I'm sorry to say that I haven't used them seriously. If you can put together an example on bl.ocks.org for people to comment on a subsequent question on stack overflow might elicit an answer. Good question.
Excellent tutorial! Thank you very much!
ReplyDeleteHi, thanks for this. I just used your tutorial and your code to add a tool tip to my visualisation. It was really very helpful.
ReplyDeleteThanks, P
Bravo!! Works great. Thanks so much!
ReplyDeleteIt should be noted that there's a functionality for creating tooltips embedded in d3:
ReplyDeletehttp://bl.ocks.org/Caged/6476579
Thanks! Those tool tips look great. Am I right in thinking that they require the downloading of the d3.tip script? If so I would hesitate to describe them as embeded. However, they do look nice! There are actually a heap of different ways to skin the tooltip cat. The one described above is pretty handy because of the HTML element, but I probably prefer http://www.d3noob.org/2014/07/my-favourite-tooltip-method-for-line.html (although it's not ideal in all instances). Thanks for the comment.
DeleteTY for the instructions. May I ask for advice? On mouseover, my code does not keep the URL in view when moving the cursor to the URL. The URL is clickable, but it fades out when moving I move the cursor. Have tried different durations but not getting the same results shown in your chart example. Here is a gist https://gist.github.com/gigster99/63e28838793dcc498339 that also includes a link to the test case (bottom). How should I update the code to achieve the same results shown in chart?
ReplyDeleteAhh.... Good question and sorry for the inexcusable delay in replying. In your case (and the one above) the url fades because when the mouse moves off the object it will immediately try to transition away. However, you may note that there is an additional link in the post (which I added in 201) which links to a new post showing how to achieve something very similar to what youy're looking for http://www.d3noob.org/2014/05/including-html-link-in-d3js-tool-tip.html. Check that out and see how you get on
DeletePerfect, thanks bro.
ReplyDelete