We add a tooltip to our histogram. This involves creating a tooltip, updating its contents to show information about the hovered bar, and moving above the hovered bar.
Let's add interactions to the histogram that we created in Module 3.
Our goal in the section is to add an informative tooltip that shows the humidity range and day count when a user hovers over a bar.
We could use d3 event listeners to change the bar's color on hover, but there's an alternative: CSS hover states. To add CSS properties that only apply when an element is hovered over, add
:hover after the selector name. It's good practice to place this selector immediately after the non-hover styles to keep all bar styles in one place.
Let's add a new selector to the
Let's have our bars change their fill to purple when we hover over them.
Great, now our bars should turn purple when we hover over them and back to blue when we move our mouse out.
Now we know how to implement hover states in two ways: CSS hover states and event listeners. Why would we use one over the other?
Since we need to update our tooltip text and position when we hover over a bar, let's add our
mouseleave event listeners at the bottom of our
bars.js file. We can set ourselves up with named functions to keep our chained code clean and concise.
Starting with our
onMouseEnter() function, we'll start by grabbing our tooltip element. If you look in our
We can use classes in multiple places (if we wanted to style multiple elements at once) but we'll only use an id in one place. This ensures that we're selecting the correct element in our chart code
We want to separate our chart manipulation code and our styling code — we should be able to move our chart hook without affecting the styles.
If we open up our styles.css, we can see our basic tooltip styles, including using a pseudo-selector
.tooltip:before to add an arrow pointing down (at the hovered bar). Also note that the tooltip is hidden (
opacity: 0) and will transition any property changes (
transition: all 0.2s ease-out). It also will not receive any mouse events (
pointer-events: none) to prevent from stealing the mouse events we'll be implementing.
Let's comment out the
opacity: 0 property so we can get a look at our tooltip.
We can see that our tooltip is positioned in the top left of our page.
If we position it instead at the top left of our chart, we'll be able to shift it based on the hovered bar's position in the chart.
We can see that our tooltip is absolutely positioned all the way to the left and 12px above the top (to offset the bottom triangle). So why isn't it positioned at the top left of our chart?
Absolutely positioned elements are placed relative to their containing block. The default containing block is the
<html> element, but will be overridden by certain ancestor elements. The main scenario that will create a new containing block is if the element has a
position other than the default (
static). There are other scenarios, but they are much more rare (for example, if a
transform is specified).
This means that our tooltip will be positioned at the top left of the nearest ancestor element that has a set
position. Let's give our
.wrapper element a position of
Perfect! Now our tooltip is located at the top left of our chart and ready to be shifted into place when a bar is hovered over.
Let's start adding our mouse events in
bars.js by grabbing the existing tooltip using its id (
#tooltip). Our tooltip won't change once we load the page, so let's define it outside of our
Now let's start fleshing out our
onMouseEnter() function by updating our tooltip text to tell us about the hovered bar. Let's select the nested
#count element and update it to display the y value of the bar. Remember, in our histogram the y value is the number of days in our dataset that fall in that humidity level range.
Looking good! Now our tooltip updates when we hover over a bar to show that bar's count.