Doing a hover event is simple with a circle - you just say hey, when I mouse over the circle, do something! It’s different with a line, though, because they’re generally very very thin. As a result, you need to use a d3.bisector
to say “what’s the closest data point?”.
You can play around with it by clicking “Open example in new window,” but I’ve also put a downloadable version over here .
Open example in new window
...
// Draw your SINGLE line (but remember it isn't called a line)
svg . append ( "path" )
. datum ( datapoints )
. attr ( "fill" , "none" )
. attr ( "stroke" , "black" )
. attr ( "d" , line );
// Create the element that will be our tooltip
// by default it's hidden with display: none
// DO NOT GIVE IT THE CLASS TOOLTIP
// IT WILL CONFLICT WITH SOME BOOTSTRAP THING
// AND WILL NOT SHOW UP AND YOU'LL BE VERY
// VERY CONFUSED AND VERY VERY SAD
var tooltip = svg . append ( "g" )
. attr ( "class" , "tip" )
. style ( "display" , "none" );
// give the tooltip a circle to highlight our
// data point
tooltip . append ( "circle" )
. attr ( "r" , 3 );
// give the tooltip a text element, but push
// it to the right and down little bit
tooltip . append ( "text" )
. attr ( "dx" , 5 )
. attr ( "dy" , 15 );
// draw an invisible rectangle over the ENTIRE page
// but even though it's invisible, make it catch
// everything your pointer (mouse) does
svg . append ( "rect" )
. attr ( "fill" , "none" )
. style ( "pointer-events" , "all" )
. attr ( "width" , width )
. attr ( "height" , height )
. on ( "mousemove" , function ( d ) {
// When the mouse is moved on top of the rectangle,
// compute what the data point is and where to draw it
// If you'd understand better, console.log all of these variables
// as we step through the process
// STEP ONE: Get the position of the mouse - how many pixels
// to the right is it?
var mouse = d3 . mouse ( this );
var mousePositionX = mouse [ 0 ];
// STEP TWO: Use the x position scale BACKWARDS to estimate
// the number of years for our mouse position
// if we're 200 pixels out, how many years would that be?
var mouseYear = xPositionScale . invert ( mousePositionX );
// STEP THREE: We have a year, but it's probably not exactly
// on one of our data points. The bisector will take the
// year we're at and round it down to the closest 'actual' year
//
// e.g. our year is 1973 but we only have 1970 and 1975, so it
// figures that 1970 is the one to the left gives us back 1970
//
// (but it doesn't actually give us 1970 as a value, or even
// tell us what our datapoint is - it gives us back the index,
// saying "it's the 12th element")
var index = d3 . bisector ( function ( d ) { return d . Year ; })
. left ( datapoints , mouseYear );
// STEP FOUR: Use the index to get the datapoint
var d = datapoints [ index ];
// STEP FIVE: What's the x and y of that datapoint? Let's
// move the tooltip to there
var xPos = xPositionScale ( d . Year );
var yPos = yPositionScale ( d . life_expectancy );
d3 . select ( ".tip" )
. attr ( "transform" , "translate(" + xPos + "," + yPos + ")" );
// STEP FIVE: Use the datapoint information to fill in the tooltip
d3 . select ( ".tip" ). select ( "text" ). text ( d . life_expectancy + " years" )
})
. on ( "mouseout" , function ( d ) {
// Hide the tooltip when you're moving off of the visulization
svg . select ( ".tip" ). style ( "display" , "none" )
})
. on ( "mouseover" , function ( d ) {
// Display the tooltip when you're over the rectangle
// why is it null? I dunno, stole it from
// https://bl.ocks.org/mbostock/3902569
svg . select ( ".tip" ). style ( "display" , null )
})
...
Open example in new window