Show data on mouse-over with d3.js
By: Kj Schmidt
Tags: Data Visualization, JavaScript, Data, Donut Chart, D3js

You’ve made it to a point with your data project that you’d like to add mouse over effects. First of all- congrats! You’ve made a graph! If you haven’t made a graph yet, I suggest following my donut chart tutorial. I’ll be building off of it here, so it may be a helpful reference.

Where should mouse-over code go?

To get started, let’s figure out where this code needs to be. Looking at our donut chart example, let’s find the path variable we created.

var path = svg.selectAll('path')

.data(pie(totals))

.enter()

.append('path')

.attr('d', arc)

.attr('fill', function (d, i) {

return color(d.data.title);

})

.attr('transform', 'translate(0, 0)')

This code creates the different segments of our donut chart. If we want something to happen when the user hovers over them, it should go here.

Adding a simple hover effect

To get started, let’s add an effect that changes the opacity of the segment on hover. We’re going use the .on method to add mouseover and mouseout effects to path . The mouseover changes the opacity to .85, and the mouseout changes it back to 1.

Now path looks like:

var path = svg.selectAll('path')

.data(pie(totals))

.enter()

.append('path')

.attr('d', arc)

.attr('fill', function (d, i) {

return color(d.data.title);

})

.attr('transform', 'translate(0, 0)') //Our new hover effects

.on('mouseover', function (d, i) {

d3.select(this).transition()

.duration('50')

.attr('opacity', '.85'); .on('mouseout', function (d, i) {

d3.select(this).transition()

.duration('50')

.attr('opacity', '1');

Awesome! You should see the opacity slightly change as you hover over each section. Time to move on to something a little more complex.

Showing values on hover

To show the value of each segment on hover, we’ll need to add a few more pieces than last time. Let’s start with a div where the value will go.

We need to add a div that appends to the body, but isn’t visible. I added this right before path :

var div = d3.select("body").append("div")

.attr("class", "tooltip-donut")

.style("opacity", 0);

We need to make this div visible when the user hovers over each segment, and make it disappear again when the user moves on. We’ll do this in path :

var path = svg.selectAll('path')

.data(pie(totals))

.enter()

.append('path')

.attr('d', arc)

.attr('fill', function (d, i) {

return color(d.data.title);

})

.attr('transform', 'translate(0, 0)')

.on('mouseover', function (d, i) {

d3.select(this).transition()

.duration('50')

.attr('opacity', '.85'); //Makes the new div appear on hover:

div.transition()

.duration(50)

.style("opacity", 1);

})

.on('mouseout', function (d, i) {

d3.select(this).transition()

.duration('50')

.attr('opacity', '1'); //Makes the new div disappear:

div.transition()

.duration('50')

.style("opacity", 0);

});

Now we need to put the value into the div and place it near the user’s mouse. If you just want to show the value, html(d.value) is all you need to do for that. We’ll be using d3.event.pageX and d3.event.pageY to place the div near the user’s mouse. Feel free to adjust the +/- values to get the placement right for your graph.

div.html(d.value)

.style("left", (d3.event.pageX + 10) + "px")

.style("top", (d3.event.pageY - 15) + "px");

For my donut chart, I wanted to show the percentage, not the value. My data set uses d.data.all for the totals of each data set. I added a variable called num to calculate the percentage, and then used the same code as above.

let num = (Math.round((d.value / d.data.all) * 100)).toString() + '%'; div.html(num)

.style("left", (d3.event.pageX + 10) + "px")

.style("top", (d3.event.pageY - 15) + "px");

Note: Using Math.round() when calculating percentages makes sure you get nice whole numbers.

If you’re only using one data set, or all of your data sets have the same total, you can use d.value and hardcode the total like this:

let num = (Math.round((d.value / 1038) * 100)).toString() + '%';

Our finished div and path javascript looks like this:

var div = d3.select("body").append("div")

.attr("class", "tooltip-donut")

.style("opacity", 0); var path = svg.selectAll('path')

.data(pie(totals))

.enter()

.append('path')

.attr('d', arc)

.attr('fill', function (d, i) {

return color(d.data.title);

})

.attr('transform', 'translate(0, 0)')

.on('mouseover', function (d, i) {

d3.select(this).transition()

.duration('50')

.attr('opacity', '.85');

div.transition()

.duration(50)

.style("opacity", 1);

let num = (Math.round((d.value / d.data.all) * 100)).toString() + '%';

div.html(num)

.style("left", (d3.event.pageX + 10) + "px")

.style("top", (d3.event.pageY - 15) + "px");

}) .on('mouseout', function (d, i) {

d3.select(this).transition()

.duration('50')

.attr('opacity', '1');

div.transition()

.duration('50')

.style("opacity", 0);

});

Styling the div

The styling I used is pretty minimal. Make sure you used the class .tooltip-donut in the div you created so the css will work (or change both to whatever you want, just make sure they match).

div.tooltip-donut {

position: absolute;

text-align: center;

padding: .5rem;

background: #FFFFFF;

color: #313639;

border: 1px solid #313639;

border-radius: 8px;

pointer-events: none;

font-size: 1.3rem;

}

Your chart now has hover effects! Great job!

If you have any issues and want to see my full code, check it out on github.

I’ve also written tutorial about adding hover effects and showing data on mouse-over for a scatter plot or line chart.