Scatter plot
We add a tooltip to our scatter plot. Along the way, we learn about d3.timeFormat and learn a great trick for making our tooltips feel smoother, using voronoi.
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo 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.
[00:00 - 00:14] All right, so now that we understand the basics of making a tool tip and interacting with our chart, we're going to move on and do the same thing with our scatter plot. And we're also going to learn a really neat trick for making our tool tip a little bit more seamless.
[00:15 - 00:25] So the code looks a lot like our scatter plot from before. We can see we also added this tool tip element here with the spot for the date, the humidity, and the dew point.
[00:26 - 00:40] I figured since we already have the humidity and the dew point, it might be nice to add the date as you hover over each of these dots. We also have this styles.css with the context already set for the wrapper.
[00:41 - 01:03] So if we take out this opacity zero for the tool tip, we should be able to see it in the top left corner of our chart. Let's just wait a minute for our code stand backs to clear whatever cache that they have.
[01:04 - 01:11] Okay, there we go. So it's just hanging out over here, waiting to be positioned.
[01:12 - 01:20] So let's make that invisible again. And we'll go over to this JavaScript file where we're drawing our scatter plot.
[01:21 - 01:32] We're making the dimensions, we're drawing the canvas, we're creating our x, y, and color scales. We're drawing our data.
[01:33 - 01:47] Actually, we don't need this color scale since we're just doing the flat blue. And we can also take out this, we probably have an accessory function for it.
[01:48 - 01:55] We're drawing the data, we're creating the axes and the axis labels. And then down here, we're ready to set up this interaction.
[01:56 - 02:07] So what we did before is we grabbed our data elements. So let's use that dots D through selection object.
[02:08 - 02:17] And we want to add our event listeners. So on mouse enter, we have an on mouse enter function.
[02:18 - 02:28] And then on mouse leave, we went on mouse leave function. And then we can define those functions down here just to keep things a little bit cleaner.
[02:29 - 02:41] And remember, we have an event object and the data associated with that element . And let's just copy this for the on mouse leave.
[02:42 - 02:50] And we probably won't need that data. Or the event really.
[02:51 - 03:01] All right, so nothing's happening because we're not doing anything on mouse enter and on mouse leave. So first off here, we want to populate our tooltip.
[03:02 - 03:09] Let's actually first make it visible. So up here, we want to create that D3 selection object for our tooltip.
[03:10 - 03:20] So our tooltip equals D3 select. And I think we have it with an ID of tooltip.
[03:21 - 03:26] So let's select that tooltip over here. And then let's just make it visible.
[03:27 - 03:36] So give it a style of opacity of one. Okay pretty simple.
[03:37 - 03:47] And before we forget, let's just throw it in this on mouse leave where we hide it again. So we have a nice hide and show tooltip here.
[03:48 - 03:57] So the first thing we want to do is populate the data within our tooltip. So we have this date, the humidity and the de-point.
[03:58 - 04:20] So remember we need to make this selection object from the event.currentTarget. And then we want to select those elements within our tooltip that correspond to where we want our date and our other data.
[04:21 - 04:35] So first we want the date. And let's just test this out by writing date in here.
[04:36 - 04:46] So let's just double check that we're using the right ID. And we're selecting the right thing.
[04:47 - 04:55] So let's log out what is this selection. Let's open our console down here.
[04:56 - 05:04] All right so this selection is selecting this circle element. Oh I see.
[05:05 - 05:13] So we don't want to change the date within our element that we're hovering instead. We want to change it within the tooltip.
[05:14 - 05:24] So we actually won't even need this selection so let's comment that out for now . So if we enter console log.
[05:25 - 05:38] So now if we're hovering over any of these elements we're getting a date showing up in our tooltip over here. So instead of date what we want to show is the current date of that element.
[05:39 - 05:51] So we have our data here but what we don't have is an accessor function for the date because we haven't used it before. So let's just store it in a variable.
[05:52 - 06:08] So our date is D and it should be the date key if I remember correctly. So let's just put the whatever is on the date key of our data object for that element within our tooltip.
[06:09 - 06:15] So there we go. So now we're seeing the date but it's not exactly formatted in a friendly way.
[06:16 - 06:30] So we need to do two things here. The first is turn this date string into a date time object and then we need to turn it back into a string but a string that is a little bit easier to read.
[06:31 - 06:45] So first we want our date parser function. So this is going to turn the state string into a JavaScript date object.
[06:46 - 07:00] So for this we can use D3 time parse and then we need to specify what the format of that string is. And we can see all of the different options here.
[07:01 - 07:11] We basically need to create a string that will correspond to the way that the data is formatted in our data. There's a lot of options here.
[07:12 - 07:35] The ones that you'll care about the most are things like this percentage sign D which is going to correspond to the date of the month, percentage sign M which is the month number and percentage sign Y which is the year. And there's small distinctions between lowercase and uppercase of these.
[07:36 - 07:54] When in doubt just go to the D3 time format GitHub page to look at the different options. So for this one the date is formatted with the year, the full year hyphen, the day hyphen.
[07:55 - 08:06] Actually no that's the month and then the day. And then what we want next is to make a date, a format date function.
[08:07 - 08:19] And then we're going to use D3 time format for this and we want a string that corresponds to the format that we want. So I think it'd be nice to have, let's see, the full month name.
[08:20 - 08:28] It's really easy to read. And then let's do the day of the week.
[08:29 - 08:36] It might be important. Probably isn't for the weather but I suppose you never know.
[08:37 - 08:45] And then let's do the day and then the year. Let's see what that looks like.
[08:46 - 09:06] So what we're not doing yet is to actually use these. So down here we want to first parse the date and then we want to format that date time mod object.
[09:07 - 09:17] And we want to spell our function names correctly. All right so here we're seeing October, Monday, the 22nd, 2018.
[09:18 - 09:30] One thing that kind of bugs me here is that we're getting the zero padded day and you can fix that by adding a hyphen in between the percentage sign and the format ter. It'll take off any padding like that.
[09:31 - 09:39] So now it's just Friday the 9th or Friday, Wednesday the 4th. Just a little bit easier to read.
[09:40 - 09:53] Okay, great. So the two other things that we want to add to our tooltip are we have humidity and dewpoint.
[09:54 - 10:15] So down here tooltip.select we want to add the humidity and then the text should correspond to the humidity value. So humidity is on our y-axis so we can use the y-accessor for that data point.
[10:16 - 10:35] That's looking good and we can do the same thing but for the dewpoint with the x-accessor Okay, great. And now the only thing left to do is to position our tooltip.
[10:36 - 11:02] So remember before we used a new style where we're setting the transform property. And remember we were using calc to manage the size of the tooltip and to have it positioned correctly within its own size.
[11:03 - 11:36] So we were moving it over 50% of its width and then adding the scaled x value for that data point. So it's over 50% of its width and over the number of pixels to the right our tooltip or a data point is and then we want to do the same thing for that y position.
[11:37 - 12:05] So we move it up 100% of its height and then we add the scaled y value for that data point and we're going to have to specify that this is pixels. So now when we hover a point it should be over our point and the one thing we 're missing here is we need to add the top and the left margins.
[12:06 - 12:19] So let's actually move this out into its own variable. So x is the scaled value and plus the dimensions.margin.left.
[12:20 - 12:37] And then y is going to be the same thing except we want to add the top margin. So let's see where that gets us.
[12:38 - 12:43] Okay great. So this is exactly what we had for those bars and this works.
[12:44 - 13:03] You know when we hover a data point here we know exactly what data it is, what the humidity is, what the due point is. There's something that really bugs me about when tooltips are applied this way to scatter plots which is in between each point.
[13:04 - 13:19] The tooltip kind of jitters a little bit and it's also kind of hard to focus on a specific point you really have to be exactly on that dot. And when things are close together it can be a little bit hard to specify which one of those you want.
[13:20 - 13:44] So there's a really nice trick we can use here and we call those Voronoi diagrams. So basically we want to create a structure that has shapes so anything within that shape the closest point is a single point.
[13:45 - 13:59] Maybe this is going to make a little bit more sense once we create it. So the first thing we want to do is up here at the top we want to basically create that network of shapes.
[14:00 - 14:25] So let's name it Delaunay because we're going to make a Delaunay diagram and we can use d3.delaunay the API for this is a little bit different than the normal d3 API and that's because it started as an external library and has recently been merged into the d3 core . So that's just something that's good to know.
[14:26 - 14:37] And then for this one we want to specify the first parameter is what is the data. The second one is how to get the x value and the third one is how to get the y value.
[14:38 - 14:49] So first we specify the array of data. Next we want to specify for each data point how do we get the x value.
[14:50 - 15:07] And then the same thing for the y. So let's take a look at this Delaunay object in our console.
[15:08 - 15:21] A little bit different than anything we've looked at before it's an object. It has all these funky methods and objects like Hall, triangles, half edges.
[15:22 - 15:39] What we want is to turn this into a boronoi diagram. And to do that we want to use this there should be a boronoi object in here.
[15:40 - 15:58] Which I'm not seeing it but let's double check that it's here. So we want to use that Delaunay dot boronoi method.
[15:59 - 16:03] And let's just double check that this is spitting anything out. All right great.
[16:04 - 16:09] So we have this boronoi object. We could go back to the Delaunay diagram but we don't want to.
[16:10 - 16:25] And what we need to do is let's just create a path for each of these boronoi shapes and we'll kind of get a better sense of what the heck I'm talking about. Let's shrink this console.
[16:26 - 16:34] There we go. So we're going to make a path for each of the items within this array.
[16:35 - 16:47] So that looks like the data binding we're doing up ahead. And we're going to give them a class of boronoi so we don't confuse them with anything else within our chart.
[16:48 - 17:10] Let's do a data binding, bind it to the data set and then join it with a path element. Let's first give it a class of boronoi just so it matches what we're selecting.
[17:11 - 17:39] And then what we want to do is set that D element. And so we're iterating over each item in our data set and we're going to use our boronoi here by taking the index of that data point and using boronoi.render cell for the index of it.
[17:40 - 17:44] And this is wrong. We want to select our bounds, put it in our bounds.
[17:45 - 17:57] So it's looking very dark. And let's go ahead and give it a style in here.
[17:58 - 18:10] So over a full tip, let's go ahead and say anything with the class of boronoi. Let's give it no fill.
[18:11 - 18:19] So now we can't see those paths. And then in here, actually in our styles might be a little bit easier.
[18:20 - 18:23] Let's give it a stroke of salmon. All right.
[18:24 - 18:36] So this is what we were talking about. So each shape bounded by those pink lines is one boronoi path.
[18:37 - 18:57] And we can see in the middle of each of these shapes is going to be one point and the shapes get smaller as the points get closer together. So out here, anything within this shape, any point, like say this point, the closest data point to that point is going to be the circle enclosed within that point.
[18:58 - 19:14] So you can assume that when your mouse is on top of any of those points, the closest data point is going to be that one point. So this makes the hover interactions a little bit easier.
[19:15 - 19:30] So as you hover around here, you don't have to be hovering exactly on that point. You can hover basically anywhere within this shape and know that you want to put a tool tip over this one point.
[19:31 - 19:59] So what we actually want to do is move our mouse enter and mouse leave events and put them on this boronoi element instead of this circle. So now when we hover around, we can see that the interaction is a little bit smoother and it's easier to hover over these points even if when we render them, those circles are really small.
[20:00 - 20:24] One thing that's a little bit jarring is these boronoi shapes go below our chart and that's because it has no idea what the extent of our chart is. So we can specify those by using this for an eye object and specifying the X- max and that's just going to be the size of the width of our bounds.
[20:25 - 20:39] So bounded width and then we also want to set the Y-max to dimension instead of bounded height. There we go.
[20:40 - 20:45] So now we can see we have all these shapes. We don't really want to see those shapes.
[20:46 - 21:02] The last thing we're going to do is take out that stroke color and now when we hover around our chart, we can see that tooltip. It's easier to mouse over any of these points.
[21:03 - 21:07] It's a little bit smoother. So go ahead and play around with both versions.
[21:08 - 21:34] It might be a personal choice, maybe prefer the first version to the second version. Another thing that I do sometimes is I detect how far away my mouse is from that data point and if it's more than say 100 pixels away, I don't add that tooltip because it can be a little bit weird to be hovering out here and getting that tooltip showing up.
[21:35 - 21:46] One other thing that I think is really useful here is changing the color of the hovered dot. It's a little bit dissociated where your mouse is and where that point is.
[21:47 - 22:01] So giving a color to the hovered point could be a little bit helpful. We can't do the same thing that we did before with the CSS because we're hovering the Voronalia paths instead of those dots.
[22:02 - 22:25] What we can do is, and we also don't want to change that dot color because sometimes a dot is behind another dot and you only get that half circle. And remember with SVG, the zandex works where the further down it is in the DOM , the higher it is with the zandex.
[22:26 - 22:33] You can't really use the index here. What we can do instead is create a new dot element.
[22:34 - 22:49] So what we can do is const, what do we want to name it? Let's name it, I don't know, day dot, the dot for that specific day.
[22:50 - 23:03] We want to append a circle. And then we want to give it a class.
[23:04 - 23:14] Let's just call it tooltip dot. And then we want to position it on top of that dot that we're hovering over.
[23:15 - 23:31] So let's set the zx and see why values, it's going to be the same as our tooltip. So we can actually just move this down underneath where we're moving to the bottom here.
[23:32 - 23:43] Let's grab that x and that y. Actually, you know what, this probably isn't going to work because we're adding the left and the top margin here.
[23:44 - 23:53] So let's just copy and face what we had before. Let's give it a radius, let's say six.
[23:54 - 24:05] Let's make it a little larger actually, seven. Let's set the fill to something a little bit different than the blue, let's say maroon.
[24:06 - 24:27] And then let's also set the pointer events to none because we don't want it to steal the focus from the Voronoi path we're hovering over. This is fun, wherever we move our mouse to, we're seeing a new dot showing up with that maroon color.
[24:28 - 24:45] And basically what we want to do is just take it away when we're leaving with our mouse. So let's just grab anything with that class, tooltip dot, and then remove it.
[24:46 - 24:49] So let's remove it from the DOM. There we go.
[24:50 - 25:02] So now when we move our mouse around, it's a little bit more obvious exactly which dot we're hovering over. And I think that really helps like solidify your eye right on that point that we're looking