A tooltip is a small element that provides contextual information when the user locates the pointer over an element. This allows you to provide details without cluttering the visualization. In this section, we will create the tooltip as a reusable chart but with a different structure than that in the previous examples. In the previous charts, we bound the data to a selection of containers for the charts; while in this case, the tooltip chart will be bound to the element on which the tooltip should appear. This implies that the selection argument in the charting
function contains the elements on which the tooltip will appear. In the case of the fruit chart, we will want the tooltip to appear when the user moves the pointer over the circles, follow the pointer as it moves over the circle, and disappear when the pointer leaves the circle. We will create a tooltip as a reusable chart, but instead of invoking the tooltip on a selection of containers, we will invoke the tooltip passing it a selection containing the circle under the cursor. We begin by creating the tooltip chart, bearing in mind these considerations:
function tooltipChart() { // Tooltip Attributes... // Charting function function chart(selection) { selection.each(function(d) { // Bind the mouse events to the container element d3.select(this) .on('mouseover', create) .on('mousemove', move) .on('mouseout', remove); }); } // Accessor methods... return chart; }
Here, we added listeners for the mouseover
, mousemove
, and mouseout
events on the selection argument. The data bound to each element will be passed on to the create
, move
, and remove
listeners. These functions will create, move, and remove the tooltip, respectively. To create the tooltip, we will create a div
container under the body
element and set its left and top offsets to the pointer position, plus we add a small offset, as shown in the following code:
// Create the tooltip chart var create = function(data) { // Create the tooltip container div var tooltipContainer = d3.select('body').append('div') .datum(data) .attr('class', 'tooltip-container') .call(init); // Move the tooltip to its initial position tooltipContainer .style('left', (d3.event.pageX + offset.x) + 'px') .style('top', (d3.event.pageY + offset.y) + 'px'), };
To locate the tooltip near the pointer, we need to set its position to absolute
. The pointer events' style must be set to none
so that the tooltip doesn't capture the mouse events. We set the position and other style attributes in an inline style element. We also set the style for the tooltip's title and content, as shown in the following code:
<style> .tooltip-container { position: absolute; pointer-events: none; padding: 2px 4px 2px 6px; background-color: #eee; border: solid 1px #aaa; } .tooltip-title { text-align: center; font-size: 12px; font-weight: bold; line-height: 1em; } .tooltip-content { font-size: 11px; } </style>
In the initialization function, we will create the div
container for the tooltip and add paragraphs for the title and the content. We also added the title
and content
methods with their corresponding accessors so that the user can configure the title and content based on the bound data:
// Initialize the tooltip var init = function(selection) { selection.each(function(data) { // Create and configure the tooltip container d3.select(this) .attr('class', 'tooltip-container') .style('width', width + 'px'), // Tooltip Title d3.select(this).append('p') .attr('class', 'tooltip-title') .text(title(data)); // Tooltip Content d3.select(this).append('p') .attr('class', 'tooltip-content') .text(content(data)); }); };
The chart.move
method will update the position of the tooltip as the pointer moves, changing its left and top offsets. The chart.remove
method will just remove the tooltip from the document:
// Move the tooltip to follow the pointer var move = function() { // Select the tooltip and move it following the pointer d3.select('body').select('div.tooltip-container') .style('left', (d3.event.pageX + offset.x) + 'px') .style('top', (d3.event.pageY + offset.y) + 'px'), }; // Remove the tooltip var remove = function() { d3.select('div.tooltip-container').remove(); };
We can use the tooltip in the fruit chart, and add tooltips when the user moves the pointer over the circles. We will create and configure the tooltip
function in the fruit chart closure, as follows:
function fruitChart() { // Create and configure the tooltip var tooltip = tooltipChart() .title(function(d) { return d.name; }) .content(function(d) { return d.description; }); // Attributes, charting function and accessors... return chart; }
In the charting
function, we can invoke the tooltip
function by passing the selection of the circles as an argument, as follows:
function fruitChart() { // Chart attributes... // Charting Function function chart(selection) { selection.each(function(data) { // Charting function content... // The event listeners of the tooltip should be // namespaced to avoid overwriting the listeners of the circles. circles .on('mouseover', function(d) { ... }) .on('mouseout', function(d) { ... }) .call(tooltip); }); } // Accessor methods.... return chart; }
To register multiple listeners for the same event type, we can add an optional namespace to the tooltip-related events, as follows:
// Tooltip charting function function chart(selection) { selection.each(function(d) { // Bind the mouse events to the container element d3.select(this) .on('mouseover.tooltip', create) .on('mousemove.tooltip', move) .on('mouseout.tooltip', remove); }); }
Now, we have the tooltips and highlighting enabled in the fruit chart.