Alina Khachatrian
Alina Khachatrian

Reputation: 757

D3 line chart same tick on hover gives different values

I'm trying to grab the line value at that exact point of time (x-axis) however same tick gives me different values depends from which side I'm hovering over, if its from the left it says current data at index[123]: 10586174, if I hover from the right it says current data at index[124]: 10586174. It is only a pixel difference as line stroke-width is only 3px but the values are different. It is very inconsistent and one tick should only return one value related to that date.

enter image description here

CodeSandbox

//Set up interactions
    d3.selectAll(".xAxis > .tick")
      .style("color", "#65757E")
      .on("mousemove", function (event) {
        event.path[0].style.stroke = "#0D2633";
        d3.select("line").style("z-index", 0);
        const mousePosition = d3.pointer(event, svg.node()); // gets [x,y]
        const currentDate = xScale.invert(mousePosition[0]); // converts x to current date

        const bisect = d3.bisector((d) => new Date(d.date)); // create a bisector

        const index_currentData = bisect.right(
          lineChartData.values[0].dataset,
          currentDate
        );

        if (index_currentData < lineChartData.values[0].dataset.length) {
          console.log(
            `Current data at index[${index_currentData}]: ${lineChartData.values[0].dataset[index_currentData].value}`
          );

        const comparisonDate = xScale2.invert(mousePosition[0]); // converts x to comparison date

        const index_comparisonData = bisect.right(
          comparisonData.values[0].dataset,
          comparisonDate
        );

        if (index_comparisonData < comparisonData.values[0].dataset.length) {
          const tooltipComparisonValue =
            comparisonData.values[0].dataset[index_comparisonData].value;
        }
      })

Upvotes: 2

Views: 511

Answers (1)

Vitaly Yastremsky
Vitaly Yastremsky

Reputation: 386

I can suggest this approach, when you are creating ticks, you can add data-attribute with a value, like

    d3.selectAll(".xAxis .tick text")
      .attr("dy", "25px")
      .style("color", "#65757E")
      .style("text-transform", "uppercase")
      // here
      .attr("dataValue", (d) => d);

and then when you are handle mousemove, you will take not a position because it can be inconsistent with your tick values, but attribute value that are belong to the target tick.

Inside event listener:

        const currentDate = event.target
          .closest("g")
          .querySelector("text")
          .getAttribute("dataValue");

Now because you have exact date, you can find in your dataset this value index (or just value),

// find index by comparing the date
const findIndexOfHoveredTick = lineChartData.values[0].dataset.findIndex(
  (d) => new Date(d.date).toString() === currentDate
);

// here you can apply this index to find object inside dataset
lineChartData.values.forEach((value) => {
  console.log(value.dataset[findIndexOfHoveredTick], "Line Value");
});

If you need grab it from comparisonData as well, you can do the same manipulation, or better to merge them [...lineChartData.values, ...comparisonData.values] findIndex of the same date, and do calculations.

Here is fork with changes: https://codesandbox.io/s/kind-morning-91fg8?file=/src/LineChart.js:9398-9533

Upvotes: 2

Related Questions