Adding tooltip interactivity to charts is one of the most desired features,
but also one of the tricker ones to implement. One common solution is to attach
.on('mouseover')
event handlers to each circle element. This will work okay,
except when points start overlapping. You will also start using up lots of memory
if the number of points is very large.
A more advanced technique is to use d3.geom.voronoi
. This solves the problem
of overlapping points, but you still run into performance issues. That's because
the solution involves generating path elements and attaching event handlers to
each element. You also need to recalculate the voronoi pattern if the chart
resizes.
One solution that can handle overlapping points and be performant, is to use a data structure called quadtree. You can read about quadtrees here
The advantage of quadtrees is that the point location algorithm happens completely in memory. There is no need
to generate elements on the DOM and attach event handlers to each. Even if you resize the browser window, the quadtree structure stays the same and will work. I've only created one event handler in my example: a mousemove
attached to the svg
element. All other SVG elements have pointer-events:none
set, so as to not interfere with the mousemove event.
The algorithm to highlight a point work as follows:
1. Create a quadtree from all the data points.
2. Locate the current x,y coordinates of the mouse in the SVG container.
3. Convert the x,y coordinates to the plotted coordinates. So [480px, 200px] would correspond to [0,0].
4. Use `quadtree.find()` to locate the point nearest to your mouse.
5. Highlight the found point, only if it is close enough to your mouse.
You can see the effect in the example above. It works well when points are relatively isolated, but shows some flaws when points are very close together. If a point happens to lie right on the edge of a square in the quadtree, then the mouse pretty much needs to be exactly over the point to work. With some extra effort, there are likely ways to increase the accuracy of this technique.
See my website for another implementation of this technique.