A Guide to Svelte Hover Events, Dynamic Styling, and Tooltips
Adding hover events, dynamic styling, and tooltips to bring our chart to life
This lesson preview is part of the Better Data Visualizations with Svelte course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Better Data Visualizations with Svelte, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

[00:00 - 00:09] Hey everyone, in this lesson we're going to add interactivity to our scatter plot to bring our chart to the next level. In particular, this is going to require us to do three things.
[00:10 - 00:24] First, add event handlers, or event listeners, to each of the circles that you already see on your screen. Second, to use conditional rendering to basically only render a tooltip if and when a circle is in fact being hovered over.
[00:25 - 00:45] And third, accessibility, we'll make sure that the exact same behaviors that we 're creating using our mouse are also available for keyboard users as well. So let's start off with event handlers, because the first thing that we need to do to make these circles interactive is to actually tell the browser, hey, this circle is in fact being hovered over.
[00:46 - 01:01] And the way that we're going to do that within Sfelt is going to be an event listener. Now, this felt tutorial has documentation on how to use event handlers in line within an already existing here it's a div, but some HTML or SVG element.
[01:02 - 01:18] Here you can see we basically write on colon in the name of the handler itself, and then an inline function like this directly after to trigger some behavior. As you can see in this tutorial, if I move my mouse, the mouse move handler is in fact being triggered.
[01:19 - 01:32] And so that's exactly the type of pattern that we want to follow whenever we add event listeners to our circles. So just as an example, let's go ahead and add an on mouse over.
[01:33 - 01:47] And within here, let's go ahead and alert the browser and say hello, just to make sure this is in fact working. Now if I go here and I hover, we see that local host is telling me hello over and over and over again.
[01:48 - 01:51] So obviously we don't want to alert this. We want to console log it.
[01:52 - 02:03] And now as I hover over more and more circles, you'll see that number at the bottom of my console is in fact increasing. So we know how to basically trigger an event whenever we hover over an element.
[02:04 - 02:06] That's what we're doing here. But we don't want to console log hello.
[02:07 - 02:14] Let's actually access the data that's being rendered. The cool thing is the circle exists within this pre-existing each block.
[02:15 - 02:24] And we're iterating through each data element and printing it or rendering it as D. We're basically iterating through and each element within data is D as a reminder.
[02:25 - 02:40] So if we console log D and save this, you'll now notice that hovering over a circle shows the person that we are trying to hover over. At the very bottom left, we see Roy who studied eight hours and received a 10% on his final.
[02:41 - 02:52] At the very top, we see Sai who studied 60 hours and got a grade of 99%. And now we can actually start to see these elements in our console just by hovering over them.
[02:53 - 03:03] And it's that easy to kind of see what elements we are trying to hover over in Svelte. So rather than using a console log, let's actually assign this hover data to a variable.
[03:04 - 03:16] So within my script tag, because this is where the logic primarily should in fact live, let's go ahead and console log hovered data. Now by default, it's going to be undefined and I'm not going to give it a starting value.
[03:17 - 03:24] That's intentional. Then to make sure that this is in fact updating, I'm going to write console log hovered data.
[03:25 - 03:44] And I'm prefixing it with this dollar colon so that it updates any time hovered data updates, which means this console will trigger any time the value changes. So now down in on mouse over, rather than console logging, let's actually reset hovered data to be equivalent to D and save that.
[03:45 - 03:53] Now what you'll notice is that by default, hovered data is undefined. But as I hover over new circles, you see this object updates.
[03:54 - 04:02] And now hovered data is equivalent to the last circle that I hovered over. So now we're getting a really good feel for the first part of today's lesson, right?
[04:03 - 04:10] Which is essentially adding event listeners to each individual circle. We've added an event listener that being mouse over.
[04:11 - 04:19] And then we've updated a new variable that we've created called hovered data to be set to the hovered circle. So that's the first part of today's lesson.
[04:20 - 04:30] But you may notice that the compiler, if you have the same extension as me installed, is yelling at you for accessibility reasons. So it says mouse over must be accompanied by on focus.
[04:31 - 04:40] And this is specifically so that keyboard users can also have the same experience as those using a mouse. So if you want to, just for now, we are going to address this in the last part of this lesson.
[04:41 - 04:49] But feel free to add an identical event listener like so for an on focus event directly below it. And that will conclude the first part.
[04:50 - 05:09] And now we can get into the second part, which is rendering a tooltip. So when we talk about rendering a tooltip within Svelte, what we really want to do is conditionally render some HTML or some SVG or some element, if and when hovered data actually exists, right?
[05:10 - 05:27] And so in Svelte, there's a very convenient, very easy way to do this, which is the if block. So down here, under the SVG, this is very important under the SVG's closing tag , but before the closing tag of div, go ahead and write pound if in the condition that we want to check for.
[05:28 - 05:41] So what we're going to say is if hovered data, and this is essentially shorthand for if hovered data is not undefined or null or false. This is evaluating if the condition is truthy or falsey.
[05:42 - 05:51] And depending on that, it will actually render what's inside of hovered data. So for now, like we usually do, let's just go ahead and write hello and then end the if block.
[05:52 - 06:06] Now if I go ahead and hover over a circle, you'll see that hello, if you look very closely at the bottom here, will in fact appear. And if you wanted to, just so you know, if you wanted to add a condition for if this is false, you could write else.
[06:07 - 06:15] And then you can maybe say goodbye. And now if you refresh the page, you'll see initially it is goodbye until we hover over and then it is hello.
[06:16 - 06:29] So this is how we can use logic within our markup using Svelte if block. So yeah, it's a bit of a weird syntax with the pound if colon else forward slash if that is in fact how we will use it in this lesson.
[06:30 - 06:36] That is how Svelte wants us to use conditional rendering in our markup. We don't want to write hello, right?
[06:37 - 06:53] We want to render, you know, tooltip information based on the hovered over circle. So the naive way to do this, the simple way right now would just be to make sure this is working, let's access the name property of the hovered data of the hovered person's data.
[06:54 - 07:12] And so if hovered data exists, go ahead and render hovered data dot name, which is essentially the object property within the hovered data. So if we save that and hover over a circle, you'll see that the name appears and it updates whenever we hover over a new circle.
[07:13 - 07:31] So we know that we are in fact conditionally rendering properly. For the sake of completeness and for code, you know, for code maintenance, let 's make sure that we separate characteristically distinct bits of code into our components, just as we already did for access X and access Y, these components.
[07:32 - 07:40] Let's create a new tooltip component and render it in our if block instead. So what do we need to do to create a new component?
[07:41 - 07:46] The first thing we need to do, you may remember, is create a file. We'll call it tooltip dot felt.
[07:47 - 07:55] And then for now, let's just make it say hello. Now we have this component in our code base, but it's not yet imported into app dots felt.
[07:56 - 08:10] So let's import tooltip from and then find the path, or you can use the alias with the dollar sign like I have here and save this. And then you'll notice this is declared, but it's never read is what the compiler is now telling us.
[08:11 - 08:25] So let's go ahead and declare it down here by rendering it as a component. Now you'll notice if you refresh that once we hover, tooltip does in fact appear.
[08:26 - 08:35] So we're doing something right, but we don't want to render hello, we want to render the data. So let's pass hover data as a prop to our tooltip.
[08:36 - 08:54] Now let's just call it data within tooltip so that's a little bit more easy to read. So now that we have data, which is actually equivalent to hover data in app dots felt, we can accept it as a prop within tooltip dots felt by using the same syntax that we did in our axes, export let data.
[08:55 - 09:11] Now instead of hello, just like we did a few seconds ago, let's actually render data dot name, save that in both files and start to hover. Now we have the exact same behavior that we had with the standalone p tag, but it lives in its own component.
[09:12 - 09:16] So we can go a little bit further than just this p tag. We could render other information as well, and we could add some hierarchy here .
[09:17 - 09:27] So we could say data dot name studied, and you could put this in its own element. Got a this percent in their assignment.
[09:28 - 09:43] And so now we see the name and the percentage, for example, and then you could have another h two element that has data dot hours, hours studied. And again, we're just embedding these data points directly within our markup with these mush mustache curly braces.
[09:44 - 09:58] So we start to see some semilences of a tooltip, but obviously not as we really want a tooltip to appear not fixed at the bottom of our chart, but instead of following our mouse movement. So what do we want to do?
[09:59 - 10:16] We want to position these h ones, these h twos, and maybe we could just wrap the entire thing in a div, for example, we want to position this div according to the mouse position, or specifically actually according to the circle position that we're hovering over. So how do we do that?
[10:17 - 10:23] Well, you may notice that we have the raw values, right? We have 10, 10 percent and eight hours.
[10:24 - 10:29] So we have numbers, but they're raw numbers. They're not yet scaled numbers.
[10:30 - 10:51] And so the astute among you may realize that we need to use our pre existing x scale and y scale that we've constructed in app dots felt to turn these numbers into points on the canvas so that we can basically position their pixel perfect position according to the hovered circle. So maybe this sounds a little difficult.
[10:52 - 10:54] Let's just go ahead and do it. And I think it'll become more clear.
[10:55 - 10:59] We're going to need two new props. An x scale and a y scale.
[11:00 - 11:09] And because these are named the same as the property that we're passing in, we can just pass them in as mustache braces like this. We don't need to add the name as we do here.
[11:10 - 11:15] Let's go back into tool tip dots felt and accept them. We'll go ahead and accept x scale and y scale.
[11:16 - 11:28] And now we can start to see the actual x and y positions by using these x scale and y scale within our script. So go ahead and write using this reactive dollar label.
[11:29 - 11:45] Let's go ahead and create an x and a y by using x scale and y scale with the pre existing data that's already being hovered that data is ours and that data is great. And then if we console log x and y in tandem, what do we notice?
[11:46 - 12:05] We see that as we hover over, sure enough, we see an x position of 54.4 and a y position of 312, which if you look at this circle is exactly the circles x and y positions. And so we have the actual positions that we want to render our elements at or our tool tip at.
[12:06 - 12:18] But the question is, how do we turn these positions down here of 51 or 54 and 312? How do we get those to actually position an element?
[12:19 - 12:29] And the answer is we need to use what's called absolute position. Now if you're not familiar with CSS, absolute positioning might be a bit confusing.
[12:30 - 12:37] It might not be something you're familiar with. So it might be a good idea to take a little bit of time to learn how absolute positioning works.
[12:38 - 12:50] But on a high level, absolute positioning allows us to take these pixel values and position this element according to them. You see, traditionally documents will follow this actual document flow.
[12:51 - 12:58] So the SVG is written first and that's why it's rendered first. And then directly under it is the div because that's what's written second, right?
[12:59 - 13:14] But if we want to break out of this document flow and position these according to pixel values, instead we need to use what's called absolute positioning. Now we'll create a new style within tooltip.spelt for our tooltip.
[13:15 - 13:26] And we'll go ahead and give it a position of absolute. And then because this is referencing tooltip, we actually need to add a class of tooltip, which we'll do up here.
[13:27 - 13:42] Now if we save that and refresh, so if we hover over an element, we see our tooltip does have a position of absolute, but it's still within the document flow. If we provide other properties to our tooltip, we can start to see how absolute positioning actually works.
[13:43 - 13:53] And those properties include top, left, right, and bottom. And these are basically providing the coordinates that we want our absolutely positioned tooltip to live at.
[13:54 - 14:10] So for example, if I gave a top of zero, we would see this tooltip translate all the way to the top of the chart. And if I increase that gradually from zero to 20, we would see it go down 20 pixels, or if I went all the way to 400 pixels, it would be at the bottom of our chart, right?
[14:11 - 14:24] So essentially top vertically translates our tooltip, where zero would be at the very top. And as we increase that number, it increases the vertical translation downward of our tooltip.
[14:25 - 14:35] The equivalent translation on the horizontal axis will be left. So if we add a left of zero pixels, it would stay where it is, because it's currently at the leftmost side of our screen.
[14:36 - 14:45] But if we increase this, we would gradually see the tooltip getting pushed to the right side of our screen. So we know how to translate elements using top and left.
[14:46 - 14:55] And if we wanted to, you know, start from the bottom, or start from the right, we would use bottom and right, for example. But we want to start in the top left.
[14:56 - 15:10] So the question is, how do we instead of using these numbers that we've hard coded 50 and 60, how do we dynamically pass in X and Y? Which if you remember, if you look in our console, we already have the pixel values that we want to pass in.
[15:11 - 15:41] The answer is we can use these inline styles within Svelte, where we can essentially say style, and my GitHub co-pilot is auto-completing this for us, and it is in fact correct, where we say left is equal to X pixels, and top is equal to Y pixels. And then because these are in fact wrapped in these mustache curly braces, right, it's going to sub in the exact values that we have here, rather than rendering X or Y.
[15:42 - 15:57] So now, as I hover over these circle elements, you'll see my tooltip does in fact move, and it goes to the right position exactly where my mouse is hovering over a specific circle. So congratulations, we've made the ugliest tooltip of all time, right?
[15:58 - 16:05] There's no background, alt text looks the same, doesn't look very nice. So let's add some styling to make our tooltip better.
[16:06 - 16:15] And that's, you know, the second part of this second step is making our tooltip pretty. Now, I'm not going to give a comprehensive lesson into how to write CSS.
[16:16 - 16:28] A lot of this is you learn as you go, but for now, you can kind of just copy and paste what I've already prepared, because I think it looks pretty nice. And maybe you could also copy these styles across future projects, future tool tips you use as well.
[16:29 - 16:45] So I'm just going to go ahead and paste in some styles that I've already identified. And to give a high level overview of what's happening here, padding is essentially providing space around the element itself to basically look like it has a border of sorts .
[16:46 - 16:54] Background is just giving a background color. Box shadow is making it stand out from the background a little using what looks like a shadow.
[16:55 - 17:01] Border radius is making it curved on the edges. And then pointer events none is so that I can hover over circles.
[17:02 - 17:11] And if the text also overlaps with those circles, we actually don't want to hover over the text, right? Right now we are trying to hover over the circle.
[17:12 - 17:25] But what the browser is interpreting is a mouse over on the text element, which is why the circle is not in fact moving. So what pointer events none does is it says the user shouldn't be able to highlight this text.
[17:26 - 17:28] They shouldn't be able to hover over it. Any hover over this should be ignored.
[17:29 - 17:40] And so if there's a circle under the text, hover over that instead. So let's go ahead and save this and look how different and how much better this already looks, right?
[17:41 - 17:46] It looks a lot cleaner. There's just a much more intuitive visual appearance to this.
[17:47 - 17:50] It looks somewhat like a tool tip. It could still use some work, but it's close.
[17:51 - 18:00] The last thing I'm going to add is this transition. And the reason I add this transition is so that as we hover over new data elements, the transition between states is not abrupt.
[18:01 - 18:07] I don't want it to instantly transition from this circle to this circle. I want it to smoothly animate there.
[18:08 - 18:23] And what this transition basically does is it tells CSS to move these pixel values over the course of 300 milliseconds and ease them in, this being the easing property that we're passing. So if I save this and hover, you'll see there are tool tip moves smoothly.
[18:24 - 18:25] It looks nice. It looks as it should.
[18:26 - 18:36] So we're doing something right. We can make this tool tip a little bit prettier, again, by updating the H1 and H2 elements, which we have here, and adding some style to those.
[18:37 - 18:49] So maybe some high level, easy styles that you could use would be adjusting the font size to make the top line larger than the bottom line. So the H1 has a font size of one rem.
[18:50 - 18:53] H2 has a font size of 0.8 rem. Changing the font weight.
[18:54 - 19:01] So maybe the font weight of H1 could be 500, so it's bolder. And maybe the H2 could be 300, so it's lighter.
[19:02 - 19:12] Adding some space between H1 and H2 using margin. And then this width will come in handy later because we want to style our grade label.
[19:13 - 19:17] That's 65% is what I'm talking about. So if we save that, we see it looks much nicer.
[19:18 - 19:28] We're already getting that visual hierarchy. The last thing I want to do is style this grade label, the 34, the 49%, so that it stands out visually and looks nicer.
[19:29 - 19:42] So for that, we're going to target the span, which exists right here, right? Now just to show you what we are targeting, if I targeted the span and gave it a font size of 3 rem, now you can see what the grade label is, right?
[19:43 - 19:50] This is the span that we're going to be targeting. Now what I'm going to do is I'm going to make its font size 80% of its parent container.
[19:51 - 19:56] So it's a bit smaller than the H1. And then I'm going to add some padding.
[19:57 - 20:00] Maybe I'll do two pixels and four pixels. And I'm going to add a background.
[20:01 - 20:08] And this is something you'll see in charts whenever you want this label appearance. So I could do a background like plum, but I think that's a bit too dark.
[20:09 - 20:12] So maybe I want to add some opacity. And I don't just know this off the top of my head.
[20:13 - 20:16] I'm reading it on my other screen. I'm going to use this color instead.
[20:17 - 20:28] So that is just a little bit of this pink background. Then I'm going to make it stand out from the left by adding this margin left of two pixels.
[20:29 - 20:42] So it pushes out from the name itself. Maybe I'll add a display of inline block so that it occupies its own space.
[20:43 - 20:54] That's what that block context does. And then just some other properties that I'm used to assigning to spans like vertical aligned bottom, or you could do middle, whatever you think looks better.
[20:55 - 21:02] I'll apply border radius as well of three pixels. And then I'll make the color slightly more faint because I don't want it to take away from the name itself.
[21:03 - 21:13] So I'll do 000.7. And this is basically 70% opacity of a black color is the interpretation here.
[21:14 - 21:18] So now we have a pretty good label. I like the appearance of this.
[21:19 - 21:23] And it's rounded out our tooltip nicely. I personally am content with these tooltip styles.
[21:24 - 21:41] And I think we're ready to move on to the next part of these tooltips, which is offsetting the tooltip. And within this part of the lesson, we're going to basically handle this unique edge case on the right side of our screen, where as you hover over the rightmost circle, you can't read the data.
[21:42 - 21:54] It looks crunched and it creates this scroll bar at the bottom because it is overflowing the chart. Now what we need to do is basically account for the x and y position relative to the chart's width and height.
[21:55 - 22:07] And if that x position exceeds the width, we want to offset this tooltip left instead of right. And as a quick note, if you want to fast forward the next 10 to 15 minutes, you 're not going to lose much.
[22:08 - 22:23] But for a production ready chart, these kind of things, although they're somewhat cumbersome and annoying, are important. But if you're just trying to get the brush strokes, the fundamentals down, this is not a fundamental, this is a bit more advanced.
[22:24 - 22:33] Like that being said, let's go ahead and get into it. So what we want to do, as I said, is consider the chart width and consider the x position of the hovered circle.
[22:34 - 22:45] If the x position plus this tooltip exceeds the chart width, we want to offset left. Now what you might notice from what I just described is we need to get the chart width into our tooltip component.
[22:46 - 22:55] So let's go back into app dots felt and pass width as a prop. And we'll want to use inner width here instead of width.
[22:56 - 23:01] Oops, sorry. We want to use inner width like so.
[23:02 - 23:14] And then we'll go back into tooltip and we'll accept it like so. Now just to verify this is working, let's go ahead and console log inner width whenever we start the page.
[23:15 - 23:21] We need to prefix it with the reactive dollar label. Oh, my bad.
[23:22 - 23:29] We called it width in the parent, so we need to accept it as width. Now you'll see width is in fact 544.
[23:30 - 23:33] So this is working. We do have the chart width.
[23:34 - 23:51] And what we want to basically calculate is whether this x position is greater than 544 or more so that this x position plus the tooltip width is 544. Now you may remember in order to get our tooltip width, we need to use what we want to do.
[23:52 - 24:00] What this felt calls dimension bindings. This is exactly what we did back in app dots felt for our responsive chart, where we bound the client width to width.
[24:01 - 24:10] Okay. So back in tooltip, let's write bind client width and assign it to a new variable that we'll call tooltip width.
[24:11 - 24:19] And then up within our script over here, we'll instantiate tooltip width. Tool tip width.
[24:20 - 24:36] And by default, it will be undefined, but immediately on, there we go, immediately on load, it will be assigned to the client width of the tooltip. So now let's go ahead and see what happens if we console log tooltip width and width.
[24:37 - 24:44] As we hover over an element on the right side of our screen, the chart width is always the same, 544. And tooltip width is 65.
[24:45 - 24:55] Now the other variable of interest here is this x position. In this case, it's 538, 538.56, right?
[24:56 - 25:01] So we have three variables of interest here. We have width, tooltip width and x.
[25:02 - 25:09] Go ahead and add that in the same console so you can see what I'm describing. Let's save this, refresh and hover over an element.
[25:10 - 25:20] So if we hover over demazon, what do we see? We see a width of 544, a tooltip width of 130 and an x position of 429.57.
[25:21 - 25:36] Now here our big calculation is going to be whether x, which is the circles x position, plus tooltip width, 130, is greater than width. If it is, then that would suggest that this is spilling over off the side of the screen, off the edge of our chart.
[25:37 - 25:50] And that's when we want to offset our tooltip left. So let's just go ahead and instead of console logging each individual element or variable, let's actually calculate this reactively and see what we find.
[25:51 - 26:04] So let's take away these mustache braces that we can do in line calculation and say, does width plus x exceed tooltip width? Now if we refresh, by default, it's undefined.
[26:05 - 26:14] But if I hover over an element like this one, the answer is true. Oh, I might need...
[26:15 - 26:20] What is up here? Oh, I have these mixed up.
[26:21 - 26:23] Sorry, excuse me. Ignore the last 30 seconds.
[26:24 - 26:31] Tool tip width plus x is greater than width. Now as I hover over a circle down here, we're going to see the answer as false.
[26:32 - 26:37] False, false, false, false. Because none of these elements are bleeding over on the edge of our chart.
[26:38 - 26:49] But if we hover over this top right, we actually see true. And it's the only circle which actually renders true, other than maybe done as on, because technically it does exceed the right bound right here.
[26:50 - 27:10] So for these two circles, we want to offset the tooltip left rather than right. In order to do this, we're going to use the same calculation, but basically dynamically render a new variable that we'll call x position based on whether or not the tooltip is falling off the edge of our chart.
[27:11 - 27:20] So create a new variable and call it x position. And this is going to basically say, is tooltip width plus x greater than width?
[27:21 - 27:42] If so, return x minus tooltip width, which basically just means move everything to the left, exactly the amount of pixels as our tooltip is wide, otherwise return x and save that. Now if I refresh and hover over, you're not going to see any changes now because we're not using x position.
[27:43 - 28:00] But if I sub an x position on line 20, replacing the left properties pixel value and save, we now see exactly the translation that we wanted, where it starts on the right . And as I hover over top right, it actually offsets left.
[28:01 - 28:15] Denizant would also offset left. So we're using this x position variable to basically dynamically offset our tooltip left or right as needed so it never falls off the side of our chart.
[28:16 - 28:30] While we're here, we can also add a little bit of a nudge to our tooltip so that it renders and doesn't directly overlap with our circle. Essentially, we could just create new variables that we call x nudge and y n udge.
[28:31 - 28:50] Let's give an x nudge of 15 and a y nudge of 30. Now in order to account for these, we're going to use the exact same kind of syntax that we did for x position, but in the return value, which would be after the question mark and after the colon, we'll just account for x nudge.
[28:51 - 29:03] So if we're offsetting left, we'll want to push it to the left equivalent to x nudge. And if we're offsetting right, we want to offset it equivalent to x nudge in the horizontal direction, right?
[29:04 - 29:14] So we'll just add the plus or minus based on which offset direction we are in fact going. You may think you could clean up this code a little bit.
[29:15 - 29:27] We are using a ternary operator, but you could also maybe turn this into its own variable. So you could say is falling off chart.
[29:28 - 29:36] And then now this reads a little bit easier. So is falling off chart if true than this, if not than that.
[29:37 - 29:45] There are ways to improve that, but on a high level, that is what we are aiming to do. And then y position is as simple as y plus y nudge.
[29:46 - 29:51] We want to push it down equivalent to our nudge positions. So go ahead and save this.
[29:52 - 29:59] And now instead of y, we want to pass y position on line 25. And now you'll notice that the tooltip never overlaps with the circle.
[30:00 - 30:04] It appears directly in the bottom right corner of that circle. So I think this is an improvement.
[30:05 - 30:10] You start to see a little bit better what circle is being referenced. There's no overlapping.
[30:11 - 30:13] Looks quite nice. You can obviously adjust these numbers as you see fit.
[30:14 - 30:23] You might like 10 and 20. This is more of an art than a science, but have fun finding the perfect values for you.
[30:24 - 30:33] So while we're here, let's also make clear which circle is being hovered. Because although this tooltip does follow our cursor, you know, there's a lot of circles overlapping like over here.
[30:34 - 30:49] You might not be sure which one you're looking at between these three, for example. And so this is going to require us back in app.s felt to basically change the style, change these attributes based on the hovered data's information.
[30:50 - 30:55] Now what do I mean by that? We're going to use this ternary operator, which we've used before.
[30:56 - 31:04] It's this question mark, which as I've talked about before, basically says, is the left side of this true? If so, return this.
[31:05 - 31:14] If not, return what follows the colon. And we're going to use that ternary operator in a couple of different contexts to basically dynamically update certain attributes.
[31:15 - 31:30] For us, it's going to be radius and opacity. And so once we trigger, once we adapt these two attributes using the ternary operator, it'll make it very easy to see which circle is being hovered over.
[31:31 - 31:39] So the first example is the radius. Let's replace 10 with this inline ternary operator, which says is hovered data equal to d.
[31:40 - 31:46] If so, let's do 20. If not, let's do 10 and save that.
[31:47 - 31:54] Now if I hover over a circle, you'll see it does in fact get bigger, which means this is already working. Let's do the exact same thing for opacity.
[31:55 - 32:05] We'll create a new declaration right under and we'll say if hovered data is equal to d, then do one. Otherwise do 0.45.
[32:06 - 32:19] You'll notice a problem with this one though, which is that all of the circles are going to be 0.45 and if nothing is hovered, all of the circles are transparent. So what we actually need to ask is does a hovered data exist in the first place ?
[32:20 - 32:31] And if it doesn't exist, all of these circles should be regular opacity of one. So you could do this a few different ways, but the simplest way is just to add another ternary around this block.
[32:32 - 32:39] So does hovered data exist? If so, is it equal to d, then return one.
[32:40 - 33:01] If hovered data does exist, but it's not equal to d, return 0.45, otherwise just return one. So this may look like very confusing and very complicated, but essentially all we're doing here is we're just saying if hovered data exists, then look at the inner tern ary operator.
[33:02 - 33:22] If it doesn't, then everything should have an opacity of one because we're not focusing on any individual element. And if this does look confusing, just know that this type of pattern might be pretty common, might be something you commonly encounter for something like tooltip styling, where there are states that you only want to trigger if at least one element is hovered over.
[33:23 - 33:36] So I would study this a little bit, try to understand the intuition behind it, write it out yourself, write out a few toy examples where you use numbers and everything else. Just to try to understand this, I think it's a good exercise.
[33:37 - 33:42] So we save opacity. You now see that opacity does toggle, but only if an element is hovered over.
[33:43 - 33:49] And whenever nothing is hovered over, all of the opacity are still one. So we're doing something right there.
[33:50 - 33:58] And the last problem with these appearances is that they're very abrupt. These visual changes just happen instantly.
[33:59 - 34:03] They don't look very nice. So we're going to use CSS to make this look a bit nicer.
[34:04 - 34:09] Down here, we're going to target all of our circle elements within our markup and do two things. First, we'll apply a transition.
[34:10 - 34:22] Now, I've already talked a little bit about these transitions, but we're going to add a 300 millisecond transition to the radius and a 300 millisecond. Actually, let's do 500 millisecond transition to the opacity.
[34:23 - 34:31] Let's click save. Now, as I hover over elements, you'll actually notice that the circle transitions really nicely kind of pops into place and it looks really smooth.
[34:32 - 34:35] Same with these opacity transitions. Everything fades out all at once.
[34:36 - 34:49] They all take 500 seconds. The next thing I'll do is add this cursor pointer property, which is, if you've ever been on a website before and you've seen this little finger, that's essentially what cursor pointer is enabling.
[34:50 - 34:55] Now, one of the remaining issues is that we can never get rid of the tool tip. It is just constantly plaguing us.
[34:56 - 34:59] We cannot get rid of it. How do we get rid of it?
[35:00 - 35:21] Thankfully, it's very simple and we're going to revisit how we started off this lesson with an on-mouse-out event. So on-mouse-over, we update hover data to equal D and let's say on-mouse-out, we want hover data to be reset to null.
[35:22 - 35:32] Now, as I hover, you see it as I leave, it disappears. This is going to say that if you're going to use mouse-out, you should also use on-blur.
[35:33 - 35:44] We could also just use the mouse-leave property, which handles both, I believe, and gets rid of that compiler error. So on-mouse-leave, hover data disappears.
[35:45 - 36:02] Now we have this really nice effect where it appears, disappears, appears, disappears the circles, gain their hover state, lose their hover state on-mouse-leave. Now we've addressed most of the animation and transitions of the circles themselves, but the tool tip still appears abruptly.
[36:03 - 36:12] So if you look closely, you know, the tool tip appears and disappears instant aneously. Let's make it transition in and out using Svelte's built-in transition features .
[36:13 - 36:28] So Svelte has what's called the transition directive, which basically looks like this, where we can just apply the transition in line. And as you can see here, this applies a fade transition to this P element based on this condition.
[36:29 - 36:38] Okay? So we're going to use not fade, but instead fly to transition our tool tip in and out whenever it does in fact appear.
[36:39 - 36:48] So we're going to import fly from Svelte transition. And then in our div, where we already have all of these properties, let's apply transition a fly and hit save.
[36:49 - 37:04] Now as you hover over a tool tip, you'll see that it actually fades in and out. If you wanted to actually make this fly rather than fade, you might need some other properties, like Y of 20, or if you wanted to be the inverse of that, negative 20.
[37:05 - 37:16] Now you see it appears up and down, up and down. We could even go one level further and say, as it transitions in, make it fly in from the top, as it leaves, make it fly to the bottom.
[37:17 - 37:23] And use fly and not fade, obviously. Now as you hover and leave, you'll notice different directions.
[37:24 - 37:32] So there's a lot that we can work with here. And you start to see these really nice transitions between states using, you know, Svelte.
[37:33 - 37:44] So the last thing that I'd say before we move on to accessibility is you might want the tool tip to remain as you hover between circles, see how it disappears and reappears. That might not be what you want.
[37:45 - 37:58] So instead of adding a mouse leave here, we could add the mouse leave at a higher level. On the collection of circles or on the SVG element itself, so that, and I'll just do it so we can kind of see what that changes.
[37:59 - 38:09] So that now the tool tip still transitions between states, but it's not until I leave the SVG that hover data is reset to null. So this is personal preference.
[38:10 - 38:18] You can place your mouse leave anywhere you want to basically trigger this behavior, but just know you have the flexibility to do either in Svelte. Okay.
[38:19 - 38:30] The third and final part of this lesson we're almost done is going to be accessibility. And so for accessibility purposes, we have these mouse over, we have these, you know, hover events.
[38:31 - 38:42] We also want them to be accessible for keyboard users. And so that's exactly why we added this on focus event earlier because the compiler told us to and, or the extension told us to.
[38:43 - 38:53] And now the only thing we need to do is add a tab index. And we'll add a tab index of zero, which basically tells the browser to make this circle focusable.
[38:54 - 39:09] Because the problem right now is if I don't have that tab index and I try to tab within, right, nothing is in fact focusing. None of the circles are being focused by my tab key.
[39:10 - 39:20] And so the way that you can make an element actually tab focusable is by adding this tab index property. So you'll do tab index of zero and hit save.
[39:21 - 39:33] And now as I hover over these circles, you'll see each of them is in fact h overed. Now this, you know, the extension might say non interactive element cannot have positive tab index value.
[39:34 - 39:43] But I believe this is okay to ignore because in our case, these circles actually are interactive and that they're rendering a tooltip, right? There is in fact an event listener.
[39:44 - 40:00] And for that reason, they are interactive is my understanding of this. I wouldn't be too much of a purist on these rules if I found for user behavior, if like keyboard users literally couldn't focus on my elements otherwise, I think that takes priority over what the extension says.
[40:01 - 40:09] So any accessibility experts who know better than me, feel free to write me about this. But this is my best understanding about these tab indexes, about these extensions.
[40:10 - 40:28] So probably the only other problem that we could address is these elements are kind of randomly ordered. So what we can do is go ahead and sort the data up here according to, you know, their x axis position so that the tabs actually go into meaningful order rather than what might be like alphabetically right now or just totally random.
[40:29 - 40:38] So we'll add data dot sort. And then the syntax for this is a comma B, a dot grade minus B dot grade.
[40:39 - 40:41] And then that ends the sort. So you might be like, what is this syntax?
[40:42 - 40:50] This is just the basic syntax of sorting. But now if I tab, you'll see we follow the actual x axis order of these circles .
[40:51 - 40:58] So all together, we've taken our, you know, static chart that was already responsive but had no interactivity. And we've added a lot of interactivity.
[40:59 - 41:17] We've made it chock full of really meaningful interactions, visually distinct interactions that are accessible for both mouse users and keyboard users. So in the next really quick lesson, we're going to add some final polish to this chart so it's ready to publish and finally finish module one.
[41:18 - 41:18] So I'm really excited and I'll see you there.