I am trying to implement the following example inside a Recharts LineChart: the Tooltip value is relative to the blue point, because my mouse happens to be near it, and further away from the grey point with the same x-value. If I move the mouse closer to the grey point, the tooltip content changes.
However, all available examples show that a Recharts Tooltip receives data about all the data series being drawn and that it seems not possible to discriminate the point nearest to the mouse, so that the Tooltip may provide its value only.
Is there a way to specify for which dot I want to send data to the Tooltip?
At the end of a long fruitless search, I decided to solve this problem myself. The minimal code is published in this Github gist.
The basic problem to solve is that any standard Recharts tooltip receives information about:
It is necessary therefore to feed the custom tooltip also with y-axis mouse position information expressed in pixels on the chart canvas. The tooltip can then calculate which data series is closest to the vertical mouse position and display only the value belonging to that data series.
Extracting the y-position in pixels is tricky, because Recharts changes the mapping between pixel and ordinate values each time it redraws the chart. But there is a chart component that must know very well this mapping, in order to place itself at the right vertical position and display the corresponding real-world ordinate value: that's every tick on the y-axis.
Problem is: how do we plug into the Recharts drawing workflow in order to get to know the mapping?
Here's how: the tick property of the Recharts
YAxis
component allows to provide a custom React component, albeit not documented with examples. This custom component is instantiated one time for each tick that Recharts decides to place on the y-axis. By trial and error I found out that my custom Tick component receives the following properties:Where
x
andy
are the cartesian coordinates of the tick (canvas pixels) andpayload
is such an object:Where
value
is expressed in real-world y-axis units.The idea is to find out the couple
(y, value)
for the lowest and highest tick in each drawing and calculate the conversion factor between pixels and real values. This will allow the custom tooltip to perform the computations mentioned above. (Strictly speaking it would be enough to collect two couples from the first two ticks that are instantiated at each chart repaint, but choosing the two most far apart gives more precision)The whole algorithm is divided among three components:
a tooltipCollector: this is a JavaScript module that presents two methods:
collect(value, y)
, invoked by the customized tick, that stores all couples(y, value)
in a private array_collection
maxAndMin()
, invoked by the custom tooltip, that reads the_collection
array and returns the couple of items in the collection that represent the lowest and highest ticks (watch out that vertical pixel values in a canvas are measured upside down!)a CustomizedTick React component that:
y
andpayload.value
to the collector by invoking itscollect(y, value)
methody
(to place itself at the right vertical position) andpayload.value
(to display the user the real-world value the tick indicates)a CustomTooltip React component that:
maxAndMin()
methodcoordinate.y
) whether it's close enough to one of the chart data series, using a threshold value; this ensures that the tooltip is drawn only when the mouse cursor is very close to a point on the graphThe code in my gist has been simplified to remove all unnecessary JSX markup. It presents a chart component that puts at work all the above mentioned components.
Please note that the standard Recharts behaviour of highlighting all the data series' points to which the tooltip abscissa is pointing has not been changed. It is therefore good practice to put a color code in the tooltip content to illustrate clearly to which data series the displayed value belongs.