Adding the tooltip

Onto our last step: adding interactions! The tooltip we'll build will be unlike any you've seen before - it will follow the mouse all the way around our chart, highlight the relevant data elements, and describe the weather for that hovered date.

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the Fullstack D3 Masterclass course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Fullstack D3 Masterclass with a single-time purchase.

Thumbnail for the \newline course Fullstack D3 Masterclass
  • [00:00 - 00:22] Okay, so here's the really fun part is we're going to add interactions to our radial weather chart. So what our goal is is when a user moves their mouse around, we trace from a line from the very center of the chart all the way to the outside and highlight those data elements for the specific day they're hovering over.

    [00:23 - 00:42] And then we also show a tool tip that gives information about that day. So we 've got ahead and done some of the more drudgery tests. We created this set of this div that it has an idea of tool tips so the JavaScript can hook into it.

    [00:43 - 00:54] And then we have sections for all those different data elements. So the date, the temperature, the UV index, the cloud cover and the precipitation. And we also have some basic styles for this.

    [00:55 - 01:20] One thing we're going to add is for the wrapper to be positioned as position relative just so our tool tip is positioned within that wrapper. So we're going to go ahead and position it relative. This isn't going to do anything to the wrapper. It'll just make sure that the tool tip inside of it is positioned relative to the bounds of that wrapper.

    [01:21 - 01:37] So we have the tool tip here and if we take out this opacity zero, we can kind of see what it looks like. Go ahead and sand back so I want to see it.

    [01:38 - 02:03] Maybe just save and refresh. So it's positioned absolutely within the chart to the top left corner and then we're going to transform it inside of the chart. So it's kind of positioned like our other chart elements. It has a set width and then there's this basic styles of padding, a background color font size is a little bit smaller.

    [02:04 - 02:29] And we also have the Z index set to 10 so it sits on top of the chart and then pointer events to none so it doesn't seal the mouse listener when we're hovering over that chart. Alright, so the next thing we need to do is actually set up these interactions in the JavaScript. So the first thing we want to do is create a circle that sits on top of our chart that'll listen for these mouse move events.

    [02:30 - 02:51] So let's call this listener circle and we just want to append to the bounds and use circle element. And this actually is going to be positioned in the right place because our bounds start the top left corner right in the center of that circle. So all we need to do is set the radius.

    [02:52 - 03:05] And for the radius, let's have it span actually the entire chart so that it's covering everything. So the since it's a radius we want the width divided by two.

    [03:06 - 03:17] So now we have this big circle covering everything on the chart. Let's give it a class name so we can take that fill away.

    [03:18 - 03:28] So let's give it a class name of let's go with. I think a listener circle works here.

    [03:29 - 03:41] And then if we grab that string and go over to our CSS. Let's give it a fill of transparent and it's something to give it a fill of none.

    [03:42 - 03:51] But that actually won't capture any mouse events. So a transparent fill still kind of draws it but you just can't see it.

    [03:52 - 03:57] Alright, so now we have our listener circle. Let's go ahead and create those event listeners.

    [03:58 - 04:06] So on mouse move. So whenever the mouse is moved over it, let's call this on mouse move function.

    [04:07 - 04:23] And while we're at it, let's add a on mouse leave function as well. And this just makes it, it sets us up so that we can hide our tool tip when the user leaves the area with their mouse.

    [04:24 - 04:36] So function and this is going to have an event. And we want to do the same thing but with on mouse leave.

    [04:37 - 04:42] We actually won't need that event. Okay, so now we have our functions.

    [04:43 - 04:56] First we need to grab our tool tip element so that we can change it. So tool tip equals D3 select and then just grab it had an ID of tooltip.

    [04:57 - 05:11] And the first thing we can do is just have it be visible. So tooltip.style set the opacity to one and then the opposite when the mouse leaves.

    [05:12 - 05:30] So let's go ahead and make sure that that is working which it should be. If it's not, we can double check that this ID is correct and that our selector is selecting anything.

    [05:31 - 05:42] Oh and the other thing we need to do is set this as a CSS style because it's not an attribute. And if it wouldn't override that CSS style in the style sheet.

    [05:43 - 05:50] Okay great. The next thing we need to do is figure out where to position our tooltip and how to draw that line.

    [05:51 - 06:02] So there's this thing called D3 pointer. It's a method that D3 has.

    [06:03 - 06:12] If we call it with nothing in there, I'm actually not sure what it'll return. Let's see.

    [06:13 - 06:19] It will complain. So basically it wants this event that's getting passed to us from the event listener.

    [06:20 - 06:26] And it's going to return the X and Y position of our mouse. So pointer is a little bit more generic.

    [06:27 - 06:37] If we were on a touch device, it would also return the position of say our finger if we were touching it. So this is giving us an X and a Y position.

    [06:38 - 06:49] So let's just save those. X and Y equals, we can destructure them into their own variables.

    [06:50 - 06:59] Let's just hide that console. So the next thing we want to do is figure out how to draw our line.

    [07:00 - 07:07] First, let's create a line that we can move around. So const tooltip line.

    [07:08 - 07:14] So bounds. This is going to be a path element.

    [07:15 - 07:27] And then let's give it a class so we can also style it. And kind of the same thing here.

    [07:28 - 07:49] We want to make it visible and make it not visible when the user is hovering and not hovering. Okay, so our goal is to get the D attribute string for drawing a line from the center all the way to the outside.

    [07:50 - 08:14] It's going to be a little bit tricky because as you can kind of see with these grid lines, the line is going to need to get bigger as it goes towards the outside because at the same angle there's more space as your further away from the center. So what we need to do first is grab the angle for a specific coordinates.

    [08:15 - 08:28] So let's create a function. It takes an X and Y value and it's going to return the angle.

    [08:29 - 08:49] And we need this because we have our X and Y but we want to get the angle at which our mouse is around the center of the circle. And to do this, we're going to need to use a 10 to not to a 10 to.

    [08:50 - 09:00] And I think in the text, there is a reference for reading more about a 10 to if you're curious, but all you need to do is pass in Y and X. And we're going to grab our angle from this.

    [09:01 - 09:10] So angle equals X and Y. So let's go ahead and log out that angle.

    [09:11 - 09:29] All right, so as we hover around, we want zero to be up here. But it looks like zero is around over here.

    [09:30 - 09:45] So what we want to do is just add math dot pi and we want it to be a quarter turn. So times, let's see, the whole thing is two pi.

    [09:46 - 09:54] So it's going to have to be times 0.5 or divided by two. So half pi.

    [09:55 - 10:08] So now, when we hover around here, it's going to be zero. And then as we go around, it gets closer to two pi.

    [10:09 - 10:16] Oh, I see. It actually goes negative over here, which works. That works for us.

    [10:17 - 10:25] Or maybe it actually doesn't work. What we actually want to do is for this to go all the way around to two pi instead of going negative.

    [10:26 - 10:37] So let's kind of edit that. So if angle is less than zero, if it's in this top left quadrant here, let's just add two pi.

    [10:38 - 10:54] Angle equals math dot pi times two plus that angle. Alright, so now that we have this angle, let's go ahead and create that D path attribute.

    [10:55 - 11:04] And to this, we're going to use something called D3 arc. So we're going to make a generator and we're going to use D3.arc.

    [11:05 - 11:19] And D3.arc takes inner radius, outer radius, start angle, and an end angle, and it'll draw an arc for us. I've used this before for a gauge component, where you kind of get that arc shape.

    [11:20 - 11:44] So, okay, here, let's just set ourselves up. So we want an inner radius, we want an outer radius, we want an angle, start angle, and move one and end angle.

    [11:45 - 12:02] Alright, so inner radius is going to be zero because we wanted to start at the very middle of our chart. The outer radius is going to be, let's go out about 1.6 times the radius, so about where those labels are on the outside.

    [12:03 - 12:25] So dimensions, bounded radius times 1.6. The start angle is going to be our angle, and let's just give it a little bit of room on both sides, so maybe minus 0.15, and then the end angle is going to be angle plus 0.015.

    [12:26 - 12:38] And this just makes it so that it's not just a straight line. I'm also not sure if this arc generator will create anything if the start and end angle are the same.

    [12:39 - 12:57] Alright, so what we want to do is, tooltip line, we want to set that D attribute. Let's get our quotation marks right, and then we want to just pass nothing in, because we just want to create that arc.

    [12:58 - 13:07] And we want to set this to LENT, because we are changing it. Otherwise it's going to yield this.

    [13:08 - 13:29] There we go, you kind of saw it for a second there. I think what's happening is it's stealing the mouse event listener, so it's stealing our hover event.

    [13:30 - 13:45] So let's actually go ahead and give it some styles. So we named it tooltip line, and the first thing I want to do is set the pointer event to none, so it's not stealing that.

    [13:46 - 13:56] So now when we hover around, we get this line that follows our mouse, and it's also really dark. So let's also add some styles, so let's set the fill.

    [13:57 - 14:15] We have this gray we're using for our ticks. Let's set the fill opacity to something a little bit lighter, because otherwise it's just going to be overwhelming.

    [14:16 - 14:21] And then another thing we can do is set the blend mode. So right now it's kind of just sitting on top of everything.

    [14:22 - 14:32] So we want to set the mix blend mode to multiply. This is something I do if I have things overlapping.

    [14:33 - 14:47] So instead of sending the opacity like we did with the circles here, we can also set the blend mode to multiply, and you can see it's kind of highlighting the overlaps, which can be really nice. Just something to play around with.

    [14:48 - 15:00] Alright, so now we have our line showing. We can actually just keep these tooltip line things in the same place.

    [15:01 - 15:19] And now what we need to do is position our tooltip. So in order to do this, we need to grab the coordinates of the outside of this line, so outer coordinates, and we want to use our get coordinates for angle.

    [15:20 - 15:43] We can pass our angle, and then 1.6, which is this number we used for figuring out where the outside of that line is. And then what we want to do is set our tooltip style opacity to 1, and then set the transform for our tooltip.

    [15:44 - 15:56] Now this is going to be a little bit complicated, and let's go through this. Transform, and we want to translate it.

    [15:57 - 16:18] And we're going to use calc here. And remember, we want to use calc because we need to shift the tooltip left by half of its width, and also shift it right by the x position, and same for the height, 100% the height, and down by the vertical position.

    [16:19 - 16:32] So, and let's just... Let's just do it this way really quickly, and then we're going to have to revise this in a second.

    [16:33 - 17:07] So let's do -50%, which is the width plus that we need these outer coordinates. And this can be pixels, and then we want to do something similar for the height , so it's -100% plus some number, which is going to be the y position of our outer coordinates.

    [17:08 - 17:26] And then we also want this to be specified as pixels. Alright, let's see how we're feeling about this.

    [17:27 - 17:39] So we're actually not seeing anything yet, and let's just double check our parentheses. I'm always getting those wrong.

    [17:40 - 18:01] Maybe let's also just log it out. Alright, so translate calc 50% plus this number of pixels, and then calc -100% plus this number of pixels.

    [18:02 - 18:18] So what I think is happening here, because this looks right, is our tooltip is showing up, but it's not... It's actually up here, and this is because our domain, our bounds, have been shifted down.

    [18:19 - 18:40] So what we need to do is add the left margin, and also add the radius. So dimensions.margin.left, I think it's just singular, plus dimensions.bounded.

    [18:41 - 19:01] Yeah, that one here. And then we're going to need to do the same thing for our vertical position. Okay, great. So now we're seeing it in the right spot here.

    [19:02 - 19:35] So another thing we want to fix is it's kind of overshooting the edge on the left and on the right, and that's because we're positioning it based on the center of the tooltip. What we want to do is make this percent a little bit more dynamic, so that if we're at the left side, let's scootch it over, so it's positioning the left side, and if we're on the right side, it's positioning the right side of our tooltip.

    [19:36 - 20:12] So first we want to say if the exposition of our tooltip is less than -50, 50, then we want to return... let's do -100, and if it's over 50, so if it's on the right side, let's do 0, otherwise do -50, so it's positioning the center.

    [20:13 - 20:41] And let's also kind of shift it a little bit, so let's add 40 pixels here, and let's subtract 40 pixels here. Okay, there we go. So it's a little bit hard to see, but if this were on a web page, then we can see that it's not overlapping the chart.

    [20:42 - 20:59] And we can do the same thing for the vertical position, so that it's not overlapping here. So it's getting a little bit cut off now, but if it had a whole page, then this would be a really nice way to give that information without overlapping with the chart elements that you want to be looking at.

    [21:00 - 21:24] So we're actually going to do something very similar to this, instead of this right here... yep, nope, that's not what I wanted. Instead of this one right here, we want to use something like this where if it's...

    [21:25 - 21:53] if the y position is under 50, then we're going to do 40 pixels, and then minus 100% of the height, if it's over 50, it's going to be -40 pixels. And that's it, and then if it's in the middle, it's going to be -50 pixels, so let's see how this is feeling.

    [21:54 - 22:18] There we go. So now you can kind of see that we're shifting our tooltip around so that it's never covering any of those chart elements, even if it's on the bottom or the left or the right side. A little bit complicated, but this is just that next level of polish for when you get a little bit more complicated with data visualization, just say, "Hmm, this is overlapping.

    [22:19 - 22:31] What can I do to fix that?" Alright, so the next thing we need to do is grab the data point associated with that specific day that we're hovering over so that we can populate our tooltip.

    [22:32 - 22:49] So in order to do this, we want to grab the date. So if we think about the information we have, we have the angle, and so we can use our angle scale, which turns dates into angles, and we can use it backwards to turn an angle into a date.

    [22:50 - 23:13] So angle scale.invert and pass at that angle. And so one way to grab the data point associated with the state is to turn it into the string that we know we have for every single day and then find the data point that has that specific date.

    [23:14 - 23:48] So if we grab that date string, and do this one as you do a d3 time format, and then I think the string was the year and the month and then the day, and we passed that date. So that's going to be, let's log it out, this is going to be a date string that is formatted similar to the time or the date key in our data.

    [23:49 - 24:02] So here we go, we can see them start to populate down here. As our console is slowly opening up, so as we move around, we can see that date changing in that right format.

    [24:03 - 24:25] So what we can do is we can use the state string and then find a data point, and it's going to be the data set dot. So we can see that the data set is going to be the same as the data set.

    [24:26 - 24:38] So we can see that the data set is going to be the same as the data set. So we can see that the data set is going to be the same as the data set.

    [24:39 - 24:55] So now that we have that data point, let's go ahead and format the different parts of the tooltip, and this is a little bit verbose. So I'm just going to go ahead and copy paste this, and we'll walk through it.

    [24:56 - 25:07] Let's see, maybe we. Okay, yeah, so we're going to have to make a color scale, but everything else should be fairly straightforward.

    [25:08 - 25:37] So as we go around, we can see that our, our tooltip is populating with the date, the min, max, I'm sure the UV index, the cloud covered, the precision probability and the precipitation type, which also mirrors the color of that type, which is just kind of a fun touch. So this code, what it's doing is it's, it's using the selects method on our tooltip D3 selection object.

    [25:38 - 26:01] It's saying, hey, select the div with an ID of tooltip date, which matches the format in our HTML file. The scary here, and we're saying set the text to a formatted date, set the minimum temperature to, and we're using our accessor functions to grab those values out of that data point.

    [26:02 - 26:13] And then using D3 format to format them, we're saying, give me one decimal point for each of these. Each of these degrees Fahrenheit.

    [26:14 - 26:29] We're doing the same thing with the UV for the cloud cover for precipitation. For precipitation, what we're doing is we're changing it to a percent, with zero decimal points.

    [26:30 - 26:43] The precipitation type, and we're setting the color of the precipitation type otherwise, and if it doesn't exist, then we're setting it to a really, really light gray. So it's not stealing attention when there's no value there.

    [26:44 - 27:02] Okay, so the next thing we want to do is we need to figure out how, okay, we don't need to do this. It's maybe a little bit extra, but what we want to do is set the minimum and maximum temperatures to reflect the color of the gradient in the line.

    [27:03 - 27:24] Definitely unnecessary, but kind of a nice way to tie in those colors if you're using a color scale to those values in the tooltip. So the way we're going to do this is we're going to create a temperature color scale.

    [27:25 - 27:38] Which I have a feeling we already have because we have a color scale to create that gradient. So let's go up to our scales.

    [27:39 - 27:45] Let's see. Oh man, we wrote a lot of code for this.

    [27:46 - 28:12] Alright, so I think it might be this gradient color scale. So for this gradient color scale, what we're doing is we're just grabbing this interpolate yellow or red. Let's go ahead and make a temperature color scale that maps those gradient colors into the temperature values.

    [28:13 - 28:20] So we called it temperature color scale. Yep, so it's going to be a linear.

    [28:21 - 28:27] Let's actually use a sequential scale. So scale, scale, sequential.

    [28:28 - 28:41] And then for this, we want to set the domain and also an interpolator. So a sequential scale is a little bit different than a linear color scale.

    [28:42 - 28:54] And that it, you want to pass it a domain and you also want to pass it interpol ator. And the interpolator is just going to be this gradient color scale, this built in D3 color scale function.

    [28:55 - 29:25] And then for the domain, we want it to grab the same extent that we're using for our, let's actually just move this down to our scale step. So we want to use the same domain as we're using for our other scale here. So our radius is transforming temperature values into radius values.

    [29:26 - 29:40] So we can actually just grab this and use radius scale domain, which will give us that range of temperatures. So now we can use this temperature color scale here.

    [29:41 - 29:57] And we're selecting that minimum temperature and setting the color to the minimum temperature passed through this temperature color scale. So now it kind of gives a sense of like that, if there's a wide range between those two values or not.

    [29:58 - 30:14] Alright, great. So that's pretty much it for adding interactions to this weather chart. And it's pretty much it for drawing the chart in general. Let's go ahead and look at it full screen in all of it.

    [30:15 - 30:28] So glory. So you can kind of see like if you have enough room, this is a really nice way to make sure the tool tip isn't interfering with your data elements.

    [30:29 - 30:50] And it's readable and it's readable at the same time that the rest of your chart is readable. So this is just a good example of ways to kind of a funky tool tip that most people probably haven't seen before, but problem solving and figuring out a way to get that information to your user.