javascriptchartschart.js

Tooltips showing wrong data in ChartJS


I have a bar chart which shows data from an API, after data normalization i'm having issues with tooltip where if i hover a set like '9:00' the tooltip shows '8:00' data by adding to it other data which doesn't belong to that time set.

My code looks like this:

const API = [{
  "totpag": 98.04,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T08:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 96.17,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T08:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 307.87,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T08:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 164.22,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T09:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 382.21,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T09:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 56.39,
  "descrpag": "NON RISCOSSO",
  "data": "2022-02-05T09:00:00",
  "backgroundColor": "rgb(24, 244, 114)"
}, {
  "totpag": 62.16,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T09:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 270.42,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T10:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 31.62,
  "descrpag": "NON RISCOSSO",
  "data": "2022-02-05T10:00:00",
  "backgroundColor": "rgb(24, 244, 114)"
}, {
  "totpag": 126.09,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T10:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 260.54,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T10:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 289.46,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T11:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 448.67,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T11:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 595.38,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T11:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 378.44,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T12:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 226.74,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T12:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 165.18,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T12:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 154.11,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T13:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 201.39,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T13:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 24.26,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T13:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 227.75,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T16:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 10.01,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T16:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 32.51,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T16:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 253.36,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T17:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 133.95,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T17:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 101.15,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T17:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 166.33,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T18:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 298.99,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T18:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 82.96,
  "descrpag": "POS MANUALE",
  "data": "2022-02-05T18:00:00",
  "backgroundColor": "rgb(24, 114, 244)"
}, {
  "totpag": 68.47,
  "descrpag": "CONTANTI",
  "data": "2022-02-05T19:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 164.06,
  "descrpag": "BANCOMAT",
  "data": "2022-02-05T19:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 36.33,
  "descrpag": "BANCOMAT",
  "data": "2022-02-07T08:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 16.2,
  "descrpag": "CONTANTI",
  "data": "2022-02-07T08:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}, {
  "totpag": 15.5,
  "descrpag": "BANCOMAT",
  "data": "2022-02-07T09:00:00",
  "backgroundColor": "rgb(255, 64, 64)"
}, {
  "totpag": 25.43,
  "descrpag": "CONTANTI",
  "data": "2022-02-07T09:00:00",
  "backgroundColor": "rgb(167, 3, 213)"
}]



const optionsPagamentiBar = {
  responsive: true,
  maintainAspectRatio: false,
  plugins: {
    legend: {
      display: false
    },
    tooltip: {
      mode: 'index',
      intersect: 0,
      usePointStyle: true,
      callbacks: {
        label: function(context) {
          return context.dataset.label + ": " + "€" + context.parsed.y.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,').replace(/[,.]/g, m => (m === ',' ? '.' : ','));
        }
      }
    }
  },
  scales: {
    y: {
      ticks: {
        display: true,
        beginAtZero: true,
        callback: function(value, index, values) {
          return "€" + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
        }
      },
      grid: {
        drawBorder: false,
        zeroLineColor: "transparent",
      }
    },
    x: {
      display: 1,
      ticks: {
        padding: 10,
        display: true,
        fontSize: 10
      },
      grid: {
        display: false
      }
    }
  }
}


const chartBarPayments = new Chart(document.getElementById("chartBarPayments").getContext('2d'), {
  type: 'bar',
  data: {
    labels: [],
    datasets: [{
      data: [],
    }]
  },
  options: optionsPagamentiBar
});


let periodo = 'giorno'

function pagamentiPerFascia(pagamenti) {
  let datasets = [];
  pagamenti.forEach((pagamento, i) => {
    if (pagamento.totpag !== 0) {
      let date = "";
      if (periodo == "anno") {
        date = moment(pagamento.data).format("MMM YYYY");
        labels.push();
      } else if (periodo == "mese") {
        date = moment(pagamento.data).format("DD MMM");
      } else {
        date = moment(pagamento.data).format('HH:mm');
      }

      let dataset = datasets.find(data => data.label === pagamento.descrpag)
      if (dataset) {
        dataset.data.push({
          x: date,
          y: pagamento.totpag
        })
      } else {
        datasets.push({
          label: pagamento.descrpag,
          data: [{
            x: date,
            y: pagamento.totpag
          }], backgroundColor: pagamento.backgroundColor
        })
      }

    }
  })

  chartBarPayments.data = {
    datasets
  };
  chartBarPayments.update();
}

pagamentiPerFascia(API)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.min.js"></script>
<canvas id="chartBarPayments"></canvas>

If you try to hover 4rd bar from '9:00' it will show tooltip on '8:00' with some data from '9:00' but even other datasets are bugged.

What is the issue and how should i fix it?


Solution

  • This issue is caused by the gaps in the dataset. For some reason, this is confusing ChartJS. If you put in zero values for the missing values in "NON RISCOSSO" and remove the if that prevents these from being added to datasets if (pagamento.totpag !== 0) it works.

    Let me know if that is an acceptable workaround. Otherwise could possibly dig a bit further into ChartJS to figure out what's going on.

    Below is some code that adds zero values into the datasets so there are no gaps. I've had to rewrite your data normalization code.

    const API = [{
      "totpag": 98.04,
      "descrpag": "BANCOMAT",
      "data": "2022-02-05T08:00:00",
      "backgroundColor": "rgb(255, 64, 64)"
    }, {
      "totpag": 96.17,
      "descrpag": "CONTANTI",
      "data": "2022-02-05T08:00:00",
      "backgroundColor": "rgb(167, 3, 213)"
    }, {
      "totpag": 307.87,
      "descrpag": "POS MANUALE",
      "data": "2022-02-05T08:00:00",
      "backgroundColor": "rgb(24, 114, 244)"
    }, {
      "totpag": 164.22,
      "descrpag": "CONTANTI",
      "data": "2022-02-05T09:00:00",
      "backgroundColor": "rgb(167, 3, 213)"
    }, {
      "totpag": 382.21,
      "descrpag": "BANCOMAT",
      "data": "2022-02-05T09:00:00",
      "backgroundColor": "rgb(255, 64, 64)"
    }, {
      "totpag": 56.39,
      "descrpag": "NON RISCOSSO",
      "data": "2022-02-05T09:00:00",
      "backgroundColor": "rgb(24, 244, 114)"
    }, {
      "totpag": 62.16,
      "descrpag": "POS MANUALE",
      "data": "2022-02-05T09:00:00",
      "backgroundColor": "rgb(24, 114, 244)"
    }, {
      "totpag": 270.42,
      "descrpag": "POS MANUALE",
      "data": "2022-02-05T10:00:00",
      "backgroundColor": "rgb(24, 114, 244)"
    }, {
      "totpag": 31.62,
      "descrpag": "NON RISCOSSO",
      "data": "2022-02-05T10:00:00",
      "backgroundColor": "rgb(24, 244, 114)"
    }, {
      "totpag": 126.09,
      "descrpag": "CONTANTI",
      "data": "2022-02-05T10:00:00",
      "backgroundColor": "rgb(167, 3, 213)"
    }, {
      "totpag": 260.54,
      "descrpag": "BANCOMAT",
      "data": "2022-02-05T10:00:00",
      "backgroundColor": "rgb(255, 64, 64)"
    }, {
      "totpag": 289.46,
      "descrpag": "POS MANUALE",
      "data": "2022-02-05T11:00:00",
      "backgroundColor": "rgb(24, 114, 244)"
    }, {
      "totpag": 448.67,
      "descrpag": "CONTANTI",
      "data": "2022-02-05T11:00:00",
      "backgroundColor": "rgb(167, 3, 213)"
    }, {
      "totpag": 595.38,
      "descrpag": "BANCOMAT",
      "data": "2022-02-05T11:00:00",
      "backgroundColor": "rgb(255, 64, 64)"
    }, {
      "totpag": 378.44,
      "descrpag": "BANCOMAT",
      "data": "2022-02-05T12:00:00",
      "backgroundColor": "rgb(255, 64, 64)"
    }, {
      "totpag": 226.74,
      "descrpag": "CONTANTI",
      "data": "2022-02-05T12:00:00",
      "backgroundColor": "rgb(167, 3, 213)"
    }, {
      "totpag": 165.18,
      "descrpag": "POS MANUALE",
      "data": "2022-02-05T12:00:00",
      "backgroundColor": "rgb(24, 114, 244)"
    }, {
      "totpag": 154.11,
      "descrpag": "CONTANTI",
      "data": "2022-02-05T13:00:00",
      "backgroundColor": "rgb(167, 3, 213)"
    }, {
      "totpag": 201.39,
      "descrpag": "BANCOMAT",
      "data": "2022-02-05T13:00:00",
      "backgroundColor": "rgb(255, 64, 64)"
    }, {
      "totpag": 24.26,
      "descrpag": "POS MANUALE",
      "data": "2022-02-05T13:00:00",
      "backgroundColor": "rgb(24, 114, 244)"
    }, {
      "totpag": 227.75,
      "descrpag": "CONTANTI",
      "data": "2022-02-05T16:00:00",
      "backgroundColor": "rgb(167, 3, 213)"
    }, {
      "totpag": 10.01,
      "descrpag": "BANCOMAT",
      "data": "2022-02-05T16:00:00",
      "backgroundColor": "rgb(255, 64, 64)"
    }, {
      "totpag": 32.51,
      "descrpag": "POS MANUALE",
      "data": "2022-02-05T16:00:00",
      "backgroundColor": "rgb(24, 114, 244)"
    }, {
      "totpag": 253.36,
      "descrpag": "CONTANTI",
      "data": "2022-02-05T17:00:00",
      "backgroundColor": "rgb(167, 3, 213)"
    }, {
      "totpag": 133.95,
      "descrpag": "POS MANUALE",
      "data": "2022-02-05T17:00:00",
      "backgroundColor": "rgb(24, 114, 244)"
    }, {
      "totpag": 101.15,
      "descrpag": "BANCOMAT",
      "data": "2022-02-05T17:00:00",
      "backgroundColor": "rgb(255, 64, 64)"
    }, {
      "totpag": 166.33,
      "descrpag": "BANCOMAT",
      "data": "2022-02-05T18:00:00",
      "backgroundColor": "rgb(255, 64, 64)"
    }, {
      "totpag": 298.99,
      "descrpag": "CONTANTI",
      "data": "2022-02-05T18:00:00",
      "backgroundColor": "rgb(167, 3, 213)"
    }, {
      "totpag": 82.96,
      "descrpag": "POS MANUALE",
      "data": "2022-02-05T18:00:00",
      "backgroundColor": "rgb(24, 114, 244)"
    }, {
      "totpag": 68.47,
      "descrpag": "CONTANTI",
      "data": "2022-02-05T19:00:00",
      "backgroundColor": "rgb(167, 3, 213)"
    }, {
      "totpag": 164.06,
      "descrpag": "BANCOMAT",
      "data": "2022-02-05T19:00:00",
      "backgroundColor": "rgb(255, 64, 64)"
    }, {
      "totpag": 36.33,
      "descrpag": "BANCOMAT",
      "data": "2022-02-07T08:00:00",
      "backgroundColor": "rgb(255, 64, 64)"
    }, {
      "totpag": 16.2,
      "descrpag": "CONTANTI",
      "data": "2022-02-07T08:00:00",
      "backgroundColor": "rgb(167, 3, 213)"
    }, {
      "totpag": 15.5,
      "descrpag": "BANCOMAT",
      "data": "2022-02-07T09:00:00",
      "backgroundColor": "rgb(255, 64, 64)"
    }, {
      "totpag": 25.43,
      "descrpag": "CONTANTI",
      "data": "2022-02-07T09:00:00",
      "backgroundColor": "rgb(167, 3, 213)"
    }];
    
    let periodo = "giorno";
    
    const optionsPagamentiBar = {
      responsive: true,
      maintainAspectRatio: false,
      interaction: {
        intersect: false,
        mode: 'index'
      },
      plugins: {
        legend: {
          display: false
        },
        tooltip: {
          usePointStyle: true,
          callbacks: {
            label: function(context) {
              return context.dataset.label + ": " + "€" + context.parsed.y.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,').replace(/[,.]/g, m => (m === ',' ? '.' : ','));
            }
          }
        }
      },
      scales: {
        y: {
          ticks: {
            display: true,
            beginAtZero: true,
            callback: function(value, index, values) {
              return "€" + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
            }
          },
          grid: {
            drawBorder: false,
            zeroLineColor: "transparent",
          }
        },
        x: {
          display: 1,
          ticks: {
            padding: 10,
            display: true,
            fontSize: 10
          },
          grid: {
            display: false
          }
        }
      }
    }
    
    // Grafico pagamenti per fascia
    const chartBarPayments = new Chart(document.getElementById("chartBarPayments").getContext('2d'), {
      type: 'bar',
      data: {
        labels: [],
        datasets: [{
          data: [],
        }]
      },
      options: optionsPagamentiBar
    });
    
    function getColorsByPayments(pagamenti) {
      const colorScale = d3.interpolateSinebow;
      const colorRangeInfo = {
        colorStart: 0.2,
        colorEnd: 1,
        useEndAsStart: true,
      };
    
      const tipiPagamenti = pagamenti.reduce((acc, pag) => {
        if (acc.includes(pag.descrpag)) return acc;
        acc.push(pag.descrpag);
        return acc
      }, [])
      let COLORS = interpolateColors(tipiPagamenti.length, colorScale, colorRangeInfo);
    
      return COLORS.map((color, index) => {
        return {
          pagamento: tipiPagamenti[index],
          backgroundColor: color
        }
      });
    }
    
    
    function pagamentiPerFascia(pagamenti) {
      //const COLORS = getColorsByPayments(pagamenti);
      let datasets = [];
      let timePeriods = [];
      let datasetLabels = [];
      let indexedData = {};
      let backgroundColors = {};
    
      pagamenti.forEach((pagamento, i) => {
        let date = "";
        if (periodo == "anno") {
          date = moment(pagamento.data).format("MMM YYYY");
          labels.push();
        } else if (periodo == "mese") {
          date = moment(pagamento.data).format("DD MMM");
        } else {
          date = moment(pagamento.data).format('HH:mm');
        }
        if (!timePeriods.includes(date)) {
           timePeriods.push(date);
        }
        if (!datasetLabels.includes(pagamento.descrpag)) {
           datasetLabels.push(pagamento.descrpag);
        }    
        backgroundColors[pagamento.descrpag] = pagamento.backgroundColor;
        indexedData[pagamento.descrpag + date] = pagamento.totpag;
      });
      datasetLabels.forEach(label => {
        const dataset = {
          label: label,
          backgroundColor: backgroundColors[label],
          data: []
        };
        datasets.push(dataset);
        timePeriods.forEach(date => {
          dataset.data.push({
            x: date,
            y: indexedData[label+date] || 0
          })       
        });
      })  
      chartBarPayments.data = {
        datasets
      };
      chartBarPayments.update();
    }
    
    pagamentiPerFascia(API);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.min.js"></script>
    <canvas id="chartBarPayments"></canvas>