[Chartjs]-How to make datalabels of a few intersecting points display in a good way?

1👍

Well Without seeing your code I must deduce how it would look like and how I would solve it.

Well I would:

  1. Iterate over the data and look for crossing points
  2. Create a "Dictionary" for the crossing points with the desired label
    • To create the labels for the crossing points I use an newline, but one could also use any other separator, like comma or so.
  3. use the formatter function from the plugin to set the labels for the crossing point’s

Here a demo, how I would do this:
There are some comments in the code that mark the essential parts.

const data = {
    labels: ['1', '2', '3', '4', '5'], 
    datasets: [{
        label: 'DS1',
        data: [{x:1, y: 1, v:'d1'}, {x:2, y: 2, v:'d1'}, {x:3, y: 4, v:'d1'}, {x:4, y: 3, v:'d1'}], 
     }, {
        label: 'DS2',
        data: [
            {x:1, y: 1, v:'d2'}, {x:2, y: 2, v:'d3'}, {x:3, y: 2, v:'d2'}, {x:4, y: 2, v:'d2'}], 
     }, {
        label: 'DS3',
        data: [{x:1, y: 1, v:'d3'}, {x:2, y: 2, v:'d3'}, {x:3, y: 5, v:'d3'}, {x:4, y: 5, v:'d3'}], 
     }],
};

// Helper to get all values into on array
let flatDatalist = data.datasets.map( entry => entry.data).flat();

// find all overlapping points
let overlappingEntries = flatDatalist.filter( (entry, index )=>{
    return flatDatalist.some((entryInner, indexInner) => index != indexInner && entry.x == entryInner.x && entry.y == entryInner.y)
})

// create an Array with new formated label for overlapping labels
let labelsForOverlappingEntries = overlappingEntries.reduce((prev, curr) => {
    let key = `${curr.x}__${curr.y}`;
    
    if(!prev[key]){
        prev[key] = `${data.labels[curr.x]}:${curr.v}`;
    } else {
          prev[key] = `${prev[key]}\n${data.labels[curr.x]}:${curr.v}`;
    }
    
    return prev;
 }, {});

const config = {
    type: 'line',
    data: data,
    plugins:[ChartDataLabels],
    options: {
        scales: {
          y: {
            min: 0,
            max: 6,
          }
        },
        maintainAspectRatio: false,
        plugins: {
          datalabels: {
            align: 'end',
            anchor: 'end',
            offset: -1,
            formatter: function(v, ctx){
              // get a multiline label when needed
              let key = `${v.x}__${v.y}`;
              if(labelsForOverlappingEntries[key]){
                return labelsForOverlappingEntries[key];
              }
              return ctx.chart.data.labels[ctx.dataIndex] + ': '+ v.v;
            },
          }
        }
    }
};

new Chart(
    document.getElementById('chart'),
    config
);
<script src="//cdn.jsdelivr.net/npm/chart.js"></script>  
<script src="
https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0/dist/chartjs-plugin-datalabels.min.js
"></script>
<div class="chart" style="height:184px; width:350px;">
    <canvas  id="chart" ></canvas>
</div>

Disclaimer I’m not sure if this is the best way, and it is maybe abit over engineered. Nevertheless it works, I hope you can use this, or atleast it helps to find your way.

Leave a comment