[Chartjs]-Chart.js: Bar Chart Click Events

74πŸ‘

βœ…

I managed to find the answer to my question by looking through the Chart.js source code.

Provided at line 3727 of Chart.js, Standard Build, is the method .getElementAtEvent. This method returns me the "chart element" that was clicked on. There is sufficent data here to determine what data to show in a drill-down view of the dataset clicked on.

On the first index of the array returned by chart.getElementAtEvent is a value _datasetIndex. This value shows the index of the dataset that was clicked on.

The specific bar that was clicked on, I believe, is noted by the value _index. In my example in my question, _index would point to One in chart_config.data.labels.

My handleClick function now looks like this:

function handleClick(evt)
{
    var activeElement = chart.getElementAtEvent(evt);

..where chart is the reference of the chart created by chart.js when doing:

chart = new Chart(canv, chart_config);

The specific set of data that was selected by the click can therefore be found as:

chart_config.data.datasets[activeElement[0]._datasetIndex].data[activeElement[0]._index];

And there you have it. I now have a datapoint that I can build a query from to display the data of the bar that was clicked on.

AUGUST 7TH, 2021. UPDATE

There is now a method for what we are looking for. Take a look at here

56πŸ‘

Hi this is the click event under options which is getting values from x and y-axis

onClick: function(c,i) {
    e = i[0];
    console.log(e._index)
    var x_value = this.data.labels[e._index];
    var y_value = this.data.datasets[0].data[e._index];
    console.log(x_value);
    console.log(y_value);
}

13πŸ‘

I found this solution at https://github.com/valor-software/ng2-charts/issues/489

public chartClicked(e: any): void {
  if (e.active.length > 0) {
  const chart = e.active[0]._chart;
  const activePoints = chart.getElementAtEvent(e.event);
  if ( activePoints.length > 0) {
   // get the internal index of slice in pie chart
   const clickedElementIndex = activePoints[0]._index;
   const label = chart.data.labels[clickedElementIndex];
   // get value by index
   const value = chart.data.datasets[0].data[clickedElementIndex];
   console.log(clickedElementIndex, label, value)
  }
 }
}

7πŸ‘

You can use onClick like this.

var worstCells3GBoxChart = new Chart(ctx, {
                type: 'bar',
                data: {
                    labels: lbls,
                    datasets: [{
                        label: 'Worst Cells by 3G',
                        data: datas,
                        backgroundColor: getColorsUptoArray('bg', datas.length),
                        borderColor: getColorsUptoArray('br', datas.length),
                        borderWidth: 1
                    }]
                },
                options: {
                    legend: {
                        display: false
                    },
                    scales: {
                        yAxes: [{
                            ticks: {
                                beginAtZero: true
                            }
                        }]
                    },
                    onClick: function (e) {
                        debugger;
                        var activePointLabel = this.getElementsAtEvent(e)[0]._model.label;
                        alert(activePointLabel);
                    }
                }
            });

5πŸ‘

Chartjs V3.4.1

This is what worked for me in v3, after looking at solutions for older versions:

const onClick = (event, clickedElements) => {
  if (clickedElements.length === 0) return

  const { dataIndex, raw } = clickedElements[0].element.$context
  const barLabel = event.chart.data.labels[dataIndex]
  ...
}
  • raw is the value of the clicked bar.
  • barLabel is the label of the clicked bar.

You need to pass the onClick to the bar chart config:

const barConfig = {
  ...
  options: {
    responsive: true,
    onClick,
    ...
  }
}

3πŸ‘

Well done! This seems to return the data value being charted though, which in many cases might be possible to appear more than once, thus making it unclear what was clicked on.

This will return the actual data label of the bar being clicked on. I found this more useful when drilling down into a category.

chart_config.data.labels[activeElement[0]._index]

3πŸ‘

This is clearly an old question, but it rated highly in my 2023 search for "chart.js click events". So a post seemed worthwhile.

Hardly any of the posted answers work in version 4 of chart.js. For version 4 use getElementsAtEventForMode

var chartStr = '{ "type": "bar","title": "hello title", "data": {"labels": ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],"datasets": [{ "label": "# of Votes", "data": [12, 19, 3, 5, 2, 3] , "backgroundColor":["Orange","255","#333333","#666666","#999999"] }] }, "options": {"indexAxis": "y","scales": {"y": {"beginAtZero": true, "ticks":{} } } } }'

const ctx1 = document.getElementById('myChart');
var chartObject = JSON.parse(chartStr);
var myChart = new Chart(ctx1, chartObject);
ctx1.onclick = function(evt) {
  var points = myChart.getElementsAtEventForMode(evt, 'nearest', {
    intersect: true
  }, true);
  if (points.length) {
    const firstPoint = points[0];
    const label = myChart.data.labels[firstPoint.index];
    const value = myChart.data.datasets[firstPoint.datasetIndex].data[firstPoint.index];
    alert(label);
  }
};
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<canvas id="myChart"></canvas>

The way the chart.js team change/break the API between major versions is certainly bold, but it can unintentionally can make historical stackoverflow posts (that don’t specify a chart.js version) misleading and of negative help value. This seems to be the case for virtually all chart.js community help I come across.

2πŸ‘

I was able to make this work in another way.
Might not be supported, but sometimes, I find that neither the label nor the value is adequate to get me the necessary information to populate a drill-through.

So what I did was add a custom set of attributes to the data:

var ctx = document.getElementById("cnvMyChart").getContext("2d");
if(theChart != null) {theChart.destroy();}
theChart = new Chart(ctx, {
type: typ,
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datakeys: ["thefirstone","thesecondone","thethirdone","thefourthone","thefifthone","thesixthone"],
datasets: [{
    label: '# of Votes',
    data: [12, 19, 3, 5, 2, 3],

…etc

Then when I need to push the drillthrough key into another ajax call, I was able to get it with this:

var theDrillThroughKey = theChart.config.data.datakeys[activePoints[0]._index];

So I’m really not sure that it’s appropriate to be adding custom elements into the data for the Chart, but it’s working so far in Chrome, IE and Firefox. I needed to be able to put more information into the drillthrough than I really wanted displayed.

Example of the full thing: https://wa.rrdsb.com/chartExamples

Thoughts?

2πŸ‘

I had the same problem with multiple datasets, and used this workaround:

var clickOnChart = function(dataIndex){
    ...
}
var lastHoveredIndex = null;
var chart_options = {
    ...
    tooltips: {
        ...
        callbacks: {
            label: function(tooltipItem, chart) {
                var index = tooltipItem.datasetIndex;
                var value  = chart.datasets[index].data[0];
                var label  = chart.datasets[index].label;

                lastHoveredIndex = index;

                return value + "€";
            }
        }
    },
    onClick:function(e, items){
        if ( items.length == 0 ) return; //Clicked outside any bar.

        clickOnChart(lastHoveredIndex);
    }
}

0πŸ‘

Let’s say that you declared a chart using a method like so:

  window.myBar = new Chart({chart_name}, {
       type: xxx,
       data: xxx,
       events: ["click"],
       options: {
           ...
       }
  });

A good way of declaring onclick events would involve listening for the canvas click, like so:

({chart_name}.canvas).onclick = function(evt) {
     var activePoints = myBar.getElementsAtEvent(evt);
     // let's say you wanted to perform different actions based on label selected
     if (activePoints[0]._model.label == "label you are looking for") { ... }
}

0πŸ‘

In the chart options for Chart.js v3.5.1 which is latest

Check below sample code

let enterpriseChartOptions = {
    responsive:true,
    maintainAspectRatio: false,
    onClick: (c,i) => {
      console.log('Get the underlying label for click,', c.chart.config._config.data.labels[i[0].index]);
    },
    plugins: { 
      title:{
        text:'Enterprise Dashboard (Health Status of 10 stores) updated every 30 minutes',
        fontSize:20
      },
    },
    scales: {
      x: {
        display: true,
        type: 'category',
        position: 'right',
        ticks: {
          padding: 8,
        },
      },
      y: {
        display: true,
        ticks: {
          callback: function(val, index) {
            // Show the label
            return val < 1 ? "All good" : (val < 2 && val >=1) ? "Warning": val === 2 ? "Critical" : ""; 
          },
          //color: 'red',
          stepSize: 1,
          padding: 8
        }
      }
    },
    layout: {
      padding: {
          left: 20,
          right: 20,
          top: 25,
          bottom: 0
      }
    },

  };

0πŸ‘

var employeeDetailsCtx = document.getElementById("employee-details").getContext("2d");

var employee_details_data = {
    labels: ["Late Present", "On Leave", "Training", "Tour"],
    datasets: [{
        label: "Officer",
        backgroundColor: "#5A8DEE",
        data: [
            ...
        ]
    }, {
        label: "Staff",
        backgroundColor: "#4BC0C0",
        data: [
            ...
        ]
    }]
};

var myoption = {
    tooltips: {
        enabled: true
    },
    hover: {
        animationDuration: 1
    },
    onClick: function (evt, i) {
        var activePoint = employeeDetailsBarChart.getElementAtEvent(evt)[0];
        var data = activePoint._chart.data;
        var datasetIndex = activePoint._datasetIndex;
        var label = data.datasets[datasetIndex].label;
        var value = data.datasets[datasetIndex].data[activePoint._index];
        e = i[0];
        var x_value = this.data.labels[e._index];

        console.log(x_value)
        console.log(label)
        console.log(value)
    },
    animation: {
        duration: 1,
        onComplete: function () {
            var chartInstance = this.chart,
                ctx = chartInstance.ctx;
            ctx.textAlign = 'center';
            ctx.fillStyle = "rgba(0, 0, 0, 1)";
            ctx.textBaseline = 'bottom';
            this.data.datasets.forEach(function (dataset, i) {
                var meta = chartInstance.controller.getDatasetMeta(i);
                meta.data.forEach(function (bar, index) {
                    var data = dataset.data[index];
                    ctx.fillText(data, bar._model.x, bar._model.y - 5);
                });
            });
        }
    }
};

var employeeDetailsBarChart = new Chart(employeeDetailsCtx, {
    type: 'bar',
    data: employee_details_data,
    options: myoption
});

0πŸ‘

check below working code

<div class="nss_main_chart_box">
    <canvas id="myChartSite"></canvas>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.2.0/chartjs-plugin-datalabels.min.js" integrity="sha512-JPcRR8yFa8mmCsfrw4TNte1ZvF1e3+1SdGMslZvmrzDYxS69J7J49vkFL8u6u8PlPJK+H3voElBtUCzaXj+6ig==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
$(document).ready(function() {
    window.chartColors = {
        lightpink: 'rgb(255, 136, 136)',
        yellow: 'rgb(255, 222, 121)',
        green: 'rgb(157, 223, 118)',
        orange: 'rgb(255, 159, 64)',
        blue: 'rgb(54, 162, 235)',
        purple: 'rgb(153, 102, 255)',
        grey: 'rgb(231,233,237)'
    };
    var myChartSite = document.getElementById("myChartSite").getContext('2d');
    Chart.register(ChartDataLabels); 
    var myChartOnSi = new Chart(myChartSite, {
        type: "bar",
        data: {
            labels: ["Last month", "This month", "Next month"],  //here to set x-axis option
            datasets: [
                {
                    label: 'January',  //here to set top heading label name
                    backgroundColor: window.chartColors.lightpink, //here to set top heading label Color
                    data: ['150', '500', '300'], //here to set Y-axis dynamic data for chart

                },
                {
                    label: 'February',
                    backgroundColor: window.chartColors.yellow,
                    data: ["70", "20", "130"],
                },
                {
                    label: 'March',
                    backgroundColor: window.chartColors.green,
                    data: [200, 90, 70],
                },
            ]
        },
        options: {
            responsive: true,
            plugins: {
                datalabels: {
                    labels: {
                        value: {
                            color: 'blue'
                        }
                    },
                    font: {
                        weight: 'bold',
                        size: 16
                    }
                },
                legend: {
                    display: true,
                },
                title: {
                    display: true,
                    text:"Month Data",
                    padding:{
                        left:0,
                        right:0,
                        bottom:0,
                        top:20
                    },
                },
            },
            onHover: (event, chartElement) => {
                event.native.target.style.cursor = chartElement[0] ? 'pointer' : 'default';
            },
            onClick: (e, activeEls) =>{
                let datasetIndex = activeEls[0].datasetIndex;
                let dataIndex = activeEls[0].index;
                let datasetLabel = e.chart.data.datasets[datasetIndex].label;
                let value = e.chart.data.datasets[datasetIndex].data[dataIndex];
                let label = e.chart.data.labels[dataIndex];
                var chartTitleText = e.chart.titleBlock.options.text;
                console.log('chartTitleText is ',chartTitleText,'Clicked:', datasetLabel, 'Value:', value,'label',label);

                },
            scales: {
                x: {
                    display: true,
                    ticks: {
                        font: {
                            size: 16
                        }
                    },
                },
                y: {
                    display: true,
                    ticks: {
                        beginAtZero: true,
                        stepSize : 5,
                        font: {
                            size: 16
                        }
                    },
                }
            }

        }
    }); 
});



</script>

Leave a comment