apexcharts.js icon indicating copy to clipboard operation
apexcharts.js copied to clipboard

Shared tooltips do not work with Irregular Timeseries

Open jhuckaby opened this issue 6 years ago • 20 comments

First of all, thank you for an awesome chart library. I absolutely love ApexCharts!

Codepen

This issue is actually illustrated perfectly on your Irregular Timeseries demo page, so no codepen is required:

https://apexcharts.com/javascript-chart-demos/area-charts/irregular-timeseries/

This demo has the following configuration for the tooltip:

tooltip: {
	shared: true
},

However, as you can see in the demo, the tooltips are not showing as shared, even if you hover over a point intersecting multiple series. Instead, it only shows a single series in the tooltip. Screenshot:

Screenshot 2019-03-20 17 27 25

Explanation

What is the behavior you expect?

I expect that when tooltip.shared is set to true, that all intersecting series will show in the tooltip hover. This is indeed the case when all series start at the same timestamp (i.e. a "regular" timeseries).

What is happening instead?

The tooltip hover is only showing one single series (the nearest one), even though multiple series intersect the X position of the mouse.

What error message are you getting?

There is no error message. This is just a visual issue.

I've noticed that when all my series data starts at the same timestamp (i.e. regular, not irregular) the shared tooltip works just fine. This issue only occurs when one or more of the series starts at a different timestamp, as shown in your Irregular Timeseries demo.

jhuckaby avatar Mar 21 '19 00:03 jhuckaby

Here is codepen created from your Irregular Timeseries demo code, but I adjusted all three series to start at the same timestamp, and have the same array length:

https://codepen.io/anon/pen/EMdpmN

As you can see, the shared tooltip works just fine in this case.

jhuckaby avatar Mar 21 '19 00:03 jhuckaby

By default, a shared tooltip is turned off in an irregular time-series. Until now, I haven't found a solution that would work elegantly in this case, hence I turned it off programmatically.

I will visit that part of code once again to see how it goes, thanks!

junedchhipa avatar Mar 21 '19 13:03 junedchhipa

Any updates on this? We have a similar issue in our graphs with 'missing' keys, but the days at the x-axes are equal.

petericebear avatar Dec 02 '19 14:12 petericebear

If the days at the x-axes are equal, you should create series with null values so that all series length stays equal.

series: [
  {
    data: [
      {
        x: new Date('17 Jul').getTime(),
        y: 20
      },
      {
        x: new Date('18 Jul').getTime(),
        y: 30
      },
      {
        x: new Date('19 Jul').getTime(),
        y: null
      }
    ]
  },
  {
    data: [
      {
        x: new Date('17 Jul').getTime(),
        y: null
      },
      {
        x: new Date('18 Jul').getTime(),
        y: 23
      },
      {
        x: new Date('19 Jul').getTime(),
        y: 98
      }
    ]
  }
],

junedchhipa avatar Jan 03 '20 06:01 junedchhipa

Wouldn't it maybe be possible to optionally show the y value at the line position, instead of the datapoint?

For context, in my example I only have data point when values are about to change or just changed. So the correct behaviour for a shared tooltip in those cases of an irregular timeseries would be to either show the y value of all series lines at the date, or to show the y value of the closest data point of each series.

onli avatar Jan 20 '20 00:01 onli

@junedchhipa can we expect an official fix for this sometime?

niklashaug avatar Jul 22 '20 14:07 niklashaug

i am stuck with this from past few days.....any update on this

betaanish avatar Jul 27 '20 19:07 betaanish

@junedchhipa ....please suggest any workaround

betaanish avatar Jul 27 '20 19:07 betaanish

@betaanish He's already suggested a workaround, just add the missing dates with null values

matthew-williamson-sd avatar Jul 28 '20 08:07 matthew-williamson-sd

The suggested fix doesn't work.

Chart


$(document).ready(function() {
    primary = [{
        x: "2020-01-05",
        y: null
    }, {
        x: "2020-10-01",
        y: 30
    }, {
        x: "2019-11-01",
        y: 15
    }, {
        x: "2020-11-02",
        y: 30
    }, ]
    secondary = [{
        x: "2020-01-05",
        y: 15
    }, {
        x: "2020-10-01",
        y: null
    }, {
        x: "2019-11-01",
        y: null
    }, {
        x: "2020-11-02",
        y: 30
    }, ]
    
    var options = {
        series: [{
            name: 'Primary',
            data: primary
        }, {
            name: 'Secondary',
            data: secondary
        }],
        chart: {
            type: 'line',
            stacked: false,
            height: 350,
            zoom: {
                type: 'x',
                enabled: true,
                autoScaleYaxis: true
            },
            toolbar: {
                autoSelected: 'zoom'
            }
        },
        dataLabels: {
            enabled: true
        },
        markers: {
            size: 0,
        },
        title: {
            text: 'Sample Series',
            align: 'left'
        },
        yaxis: {
            labels: {
                formatter: function(val) {
                    return val;
                },
            },
            title: {
                text: 'Amount'
            },
        },
        xaxis: {
            type: 'datetime',
        },
        tooltip: {
            shared: true,
            y: {
                formatter: function(val) {
                    return val;
                }
            }
        }
    };
    var chart = new ApexCharts(document.querySelector("#chart"), options);
    chart.render();
});

alfonsrv avatar Nov 02 '20 18:11 alfonsrv

facing the same problem here.

any chances it'll be fixed anytime soon?

v360-eduardo-marques avatar Nov 17 '20 12:11 v360-eduardo-marques

The same issue here, any fix so far?

Muqeet1 avatar Apr 15 '21 07:04 Muqeet1

hey everyone, I think I got the solution, and perhaps this solution will works for any case

so the key is using the "w.globals" context, and I used this flow to generate a correct tooltip:

  1. store the hovered x-axis series value (in this case is date time in integer form)
  2. find and map the index of similar time from another x-axis series from w.globals.seriesX
  3. print series value based on all x-axis points found with similar time
  4. style the tooltip, you can format the hovered x-axis series value to readable date time format as tooltip title
custom: ({ series, seriesIndex, dataPointIndex, w }) => {
      const hoverXaxis = w.globals.seriesX[seriesIndex][dataPointIndex];
      const hoverIndexes = w.globals.seriesX.map(seriesX => {
        return seriesX.findIndex(xData => xData === hoverXaxis);
      });

      let hoverList = '';
      hoverIndexes.forEach((hoverIndex, seriesEachIndex) => {
        if (hoverIndex >= 0) {
          hoverList += `<span>${w.globals.seriesNames[seriesEachIndex]}: ${series[seriesEachIndex][hoverIndex]}</span><br />`;
        }
      });
      const formatHoverX = this.dateFormat(new Date(hoverXaxis), 'YYYY-MM-DD HH:mm:ss');

      return `<div class="card">
        <div class="card-header p-1">${formatHoverX}</div>
        <div class="card-body p-1">
          ${hoverList}
        </div>
      </div>`;
    },

faizalami avatar Jun 15 '21 05:06 faizalami

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

github-actions[bot] avatar Nov 13 '21 14:11 github-actions[bot]

Hello everyone, follow up on the comment by @faizalami , this is my implementation to fix the issue. For context, I use the Vue wrapper.

        customSharedTooltip({ series, seriesIndex, dataPointIndex, w }) {
            const hoverXaxis = w.globals.seriesX[seriesIndex][dataPointIndex];
            const hoverIndexes = w.globals.seriesX.map((seriesX) => {
                return seriesX.findIndex((xData) => xData === hoverXaxis);
            });

            let hoverList = "";
            hoverIndexes.forEach((hoverIndex, seriesEachIndex) => {
                if (hoverIndex >= 0) {
                    hoverList += `
                        <div class="apexcharts-tooltip-series-group apexcharts-active" style="order: 1; display: flex;">
                            <span class="apexcharts-tooltip-marker" style="background-color: ${
                                w.globals.markers.colors[seriesEachIndex]
                            };"></span>
                            <div class="apexcharts-tooltip-text" style="font-family: Helvetica, Arial, sans-serif; font-size: 12px;">
                                <div class="apexcharts-tooltip-y-group">
                                    <span class="apexcharts-tooltip-text-y-label">${
                                        w.globals.seriesNames[seriesEachIndex]
                                    }: </span>
                                    <span class="apexcharts-tooltip-text-y-value">${w.globals.yLabelFormatters[0](
                                        series[seriesEachIndex][hoverIndex]
                                    )}</span>
                                </div>
                            </div>
                        </div>`;
                }
            });
            const parsed = new Date(hoverXaxis)
                .toDateString()
                .split(" ")
                .slice(1);
            return `<div class="apexcharts-tooltip-title" style="font-family: Helvetica, Arial, sans-serif; font-size: 12px;">${
                parsed[1] + " " + parsed[0] + " " + parsed[2]
            }</div>${hoverList}`;
        },

This is the method I defined, this way the rendering for the tooltip is identical to the default tooltip used by Apex. I really think this issue should be addressed in upstream.

Splinter0 avatar Feb 21 '22 16:02 Splinter0

@Splinter0 thanks a lot! by the way, there should be

w.globals.yLabelFormatters[seriesEachIndex]

instead of

w.globals.yLabelFormatters[0]

dmitrypasyutin avatar Jun 23 '22 20:06 dmitrypasyutin

Thank you @Splinter0 and @dmitrypasyutin . This solution temporary solves the problem. What I'm missing though, are the markers on each line when hovering. I think this might be related to the same issue.

Example: I would expect to have a marker on the green line as well: image

Is there a way to improve this ?

placid2000 avatar Oct 26 '22 09:10 placid2000

Hello everyone, follow up on the comment by @faizalami , this is my implementation to fix the issue. For context, I use the Vue wrapper.

        customSharedTooltip({ series, seriesIndex, dataPointIndex, w }) {
            const hoverXaxis = w.globals.seriesX[seriesIndex][dataPointIndex];
            const hoverIndexes = w.globals.seriesX.map((seriesX) => {
                return seriesX.findIndex((xData) => xData === hoverXaxis);
            });

            let hoverList = "";
            hoverIndexes.forEach((hoverIndex, seriesEachIndex) => {
                if (hoverIndex >= 0) {
                    hoverList += `
                        <div class="apexcharts-tooltip-series-group apexcharts-active" style="order: 1; display: flex;">
                            <span class="apexcharts-tooltip-marker" style="background-color: ${
                                w.globals.markers.colors[seriesEachIndex]
                            };"></span>
                            <div class="apexcharts-tooltip-text" style="font-family: Helvetica, Arial, sans-serif; font-size: 12px;">
                                <div class="apexcharts-tooltip-y-group">
                                    <span class="apexcharts-tooltip-text-y-label">${
                                        w.globals.seriesNames[seriesEachIndex]
                                    }: </span>
                                    <span class="apexcharts-tooltip-text-y-value">${w.globals.yLabelFormatters[0](
                                        series[seriesEachIndex][hoverIndex]
                                    )}</span>
                                </div>
                            </div>
                        </div>`;
                }
            });
            const parsed = new Date(hoverXaxis)
                .toDateString()
                .split(" ")
                .slice(1);
            return `<div class="apexcharts-tooltip-title" style="font-family: Helvetica, Arial, sans-serif; font-size: 12px;">${
                parsed[1] + " " + parsed[0] + " " + parsed[2]
            }</div>${hoverList}`;
        },

This is the method I defined, this way the rendering for the tooltip is identical to the default tooltip used by Apex. I really think this issue should be addressed in upstream.

Does anyone have this solution for React apexcharts?

raoulspronck avatar Feb 24 '23 14:02 raoulspronck

the code above still may not work if corresponding XValue is not found in other series. This fixed code finds not only equal but the most close value on X axis to the one which was hovered. Obviously this works only with numbers (e.g., timestamps) on X axis. For me this is more than enough:

function customTooltip({ series, seriesIndex, dataPointIndex, w }) {
    const hoverXaxis = w.globals.seriesX[seriesIndex][dataPointIndex];
    const hoverIndexes = w.globals.seriesX.map((seriesX) => {
      let min = Math.abs(seriesX[0] - hoverXaxis);
      let minIndex = 0;
      for (let i = 1; i < seriesX.length; i++) {
        const v = Math.abs(seriesX[i] - hoverXaxis);
        if (v < min) { 
          min = v;
          minIndex = i;
        }
       }
      return minIndex
    });

    let hoverList = "";
    hoverIndexes.forEach((hoverIndex, seriesEachIndex) => {
        if (hoverIndex >= 0) {
            hoverList += `
                <div class="apexcharts-tooltip-series-group apexcharts-active" style="order: 1; display: flex;">
                    <span class="apexcharts-tooltip-marker" style="background-color: ${
                        w.globals.markers.colors[seriesEachIndex]
                    };"></span>
                    <div class="apexcharts-tooltip-text" style="font-family: Helvetica, Arial, sans-serif; font-size: 12px;">
                        <div class="apexcharts-tooltip-y-group">
                            <span class="apexcharts-tooltip-text-y-label">${
                                w.globals.seriesNames[seriesEachIndex]
                            }: </span>
                            <span class="apexcharts-tooltip-text-y-value">${w.globals.yLabelFormatters[0](
                                series[seriesEachIndex][hoverIndex]
                            )}</span>
                        </div>
                    </div>
                </div>`;
        }
    });
    const parsed = new Date(hoverXaxis)
        .toDateString()
        .split(" ")
        .slice(1);
    return `<div class="apexcharts-tooltip-title" style="font-family: Helvetica, Arial, sans-serif; font-size: 12px;">${
        parsed[1] + " " + parsed[0] + " " + parsed[2]
    }</div>${hoverList}`;
}

stalniy avatar Mar 26 '24 02:03 stalniy