How to Show Data on Mouseover in d3.js

Data Tutorial

d3.js can be a powerful tool for data visualization, yet it’s important to understand some of the fundamental capabilities provided by the library, as well as its limitations.

In this tutorial, we’ll explore one such limitation of d3.js by adding tooltips to a visualization, which is not an inherent capability of the library. Instead, we’ll explore two examples, one using the browser’s built-in title tag and the other using a custom div attached to mouse events.

Creating a Tooltip Using the Title Tag

Easily the most basic method for displaying data that is part of a d3.js visualization when mousing over part of the graphic is to use the title tag.

While typically the title tag is found in the head section of an HTML document and determines the text title that appears in the browser tab when viewing that page, in modern browsers when title tags appear elsewhere in the document as a child of a div or other object with defined dimensions, hovering the cursor over said object will cause a simple tooltip from the browser to appear with the appropriate text.

For the purposes of displaying data in d3.js, we can take advantage of this modern browser functionality. For this example we have a relatively simple pie chart created using d3.js:

var w = 400, h = 400, r = 200, color = d3.scale.category20c();
data = [{"label":"apples", "count":20},
        {"label":"bananas", "count":50},
        {"label":"pears", "count":30}];

var vis = d3.select("body")
    .append("svg:svg")
  .data([data])
    .attr("width", w)
    .attr("height", h)
  .append("svg:g")
    .attr("transform", "translate(" + r + "," + r + ")")
var arc = d3.svg.arc()
    .outerRadius(r);
var pie = d3.layout.pie()
  .value(function(d) { return d.count; });
var arcs = vis.selectAll("g.slice")
  .data(pie)
  .enter().append("svg:g")
        .attr("class", "slice");
arcs.append("svg:path")
  .attr("fill", function(d, i) { return color(i); } )
  .attr("d", arc);
arcs.append("svg:text")
  .attr("transform", function(d) {
    d.innerRadius = 0;
    d.outerRadius = r;
    return "translate(" + arc.centroid(d) + ")";
    })
  .attr("text-anchor", "middle")
  .text(function(d, i) { return data[i].label; });

We have 3 sections that represent the number of each fruit we have. But while our data has a count value for each of our fruits, there’s no indication of that number in the chart other than the relative size of each slice.

To remedy this, we’ll add a title tag to each slice with a few simple lines of code:

.append("svg:title")
  .text(function(d) { return d.value; })

This code is appending (adding a child) title tag onto whatever we append it to, then also modifying that title tag’s text value to be equal to the value of our data (represented by the variable d, as is typical in d3.js code).

Specifically, we need to append this tag to the svg:path tag when we’re generating the arcs, so the final modified code looks like this (comments show where we inserted the title appendage):

var w = 400, h = 400, r = 200, color = d3.scale.category20c();
data = [{"label":"apples", "count":20},
        {"label":"bananas", "count":50},
        {"label":"pears", "count":30}];

var vis = d3.select("body")
    .append("svg:svg")
  .data([data])
    .attr("width", w)
    .attr("height", h)
  .append("svg:g")
    .attr("transform", "translate(" + r + "," + r + ")")
var arc = d3.svg.arc()
    .outerRadius(r);
var pie = d3.layout.pie()
  .value(function(d) { return d.count; });
var arcs = vis.selectAll("g.slice")
  .data(pie)
  .enter().append("svg:g")
        .attr("class", "slice");
arcs.append("svg:path")
  .attr("fill", function(d, i) { return color(i); } )
  .attr("d", arc)
  .append("svg:title") // TITLE APPENDED HERE
    .text(function(d) { return d.value; });
arcs.append("svg:text")
  .attr("transform", function(d) {
    d.innerRadius = 0;
    d.outerRadius = r;
    return "translate(" + arc.centroid(d) + ")";
    })
  .attr("text-anchor", "middle")
  .text(function(d, i) { return data[i].label; });

When we view this, mousing over a colored slice of the pie will display an in-browser title tooltip showing the count value for each slice. Hooray!

Creating a Tooltip Using Mouseover Events

If you want something that can be customized a bit more than the default in-browser title tooltip, then perhaps a custom div tooltip is the answer.

For this example, we’ve created a simple bar chart, but we want hovering over each to display the value in a tooltip as well.

var data = [4, 8, 15, 16, 23, 42];

var x = d3.scale.linear()
    .domain([0, d3.max(data)])
    .range([0, 420]);

d3.select("body")
  .selectAll("div")
    .data(data)
  .enter().append("div")
    .style("width", function(d) { return x(d) + "px"; })
    .text(function(d) { return d; }));

The solution is effectively to create a tooltip div within our code, then attach the appropriate mouseover, mousemove, and mouseout event functions to each of our bar chart divs to appropriately show/hide our custom tooltip div. When the tooltip is shown, we can easily grab the data we want to actually display as the text.

The full code is shown below:

var data = [4, 8, 15, 16, 23, 42];

var x = d3.scale.linear()
    .domain([0, d3.max(data)])
    .range([0, 420]);

var tooltip = d3.select("body")
    .append("div")
    .style("position", "absolute")
    .style("z-index", "10")
    .style("visibility", "hidden")
    .style("background", "#000")
    .text("a simple tooltip");

d3.select("body")
  .selectAll("div")
    .data(data)
  .enter().append("div")
    .style("width", function(d) { return x(d) + "px"; })
    .text(function(d) { return d; })
    .on("mouseover", function(d){tooltip.text(d); return tooltip.style("visibility", "visible");})
      .on("mousemove", function(){return tooltip.style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");})
      .on("mouseout", function(){return tooltip.style("visibility", "hidden");});

The critical additions are the var tooltip = ... block where we’re creating our tooltip itself, which is just a div that is hidden by default and positioned “above” all the elements on the page (using a high z-index value).

Once that is created, we’ve then added onto the bar chart creation code of d3.js using a number of .on method calls, which accept the appropriate event and the function to fire when that event occurs. The most crucial is in the first mouseover event function, where we specify tooltip.text(d), meaning the text of our tooltip is set to the passed in data value of d.

The result is some nice customizable div tooltips with the data we passed in.