This video is available to students only

Bar chart

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.

Bar chart#

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.

histogram finished

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 styles.css file.

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.

histogram with hover state

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?

CSS hover states are good to use for more stylistic updates that don't require DOM changes. For example, changing colors or opacity. If we're using a CSS preprocessor like SASS, we can use any color variables instead of duplicating them in our JavaScript file.

JavaScript event listeners are what we need to turn to when we need a more complicated hover state. For example, if we want to update the text of a tooltip or move an element, we'll want to do that in JavaScript.

Since we need to update our tooltip text and position when we hover over a bar, let's add our mouseenter and 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 index.html file, you can see that our template starts with a tooltip with two children: a div to display the range and a div to display the value. We'll follow the common convention of using ids as hooks for JavaScript and classes as hooks for CSS. There are two main reasons for this distinction:

  1. 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

  2. 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.

We could create our tooltip in JavaScript, the same way we have been creating and manipulating SVG elements with d3. We have it defined in our HTML file here, which is generally easier to read and maintain since the tooltip layout is static.

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.

histogram with visible tooltip - far away!

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 relative.

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.

histogram with visible tooltip

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 onMouseEnter() function.

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.


This page is a preview of Fullstack D3 Masterclass

Please select a discussion on the left.