图表.js:条形图点击事件

Chart.js: Bar Chart Click Events

本文关键字:事件 条形图 js 图表      更新时间:2023-09-26

我刚刚开始使用Chart.js,我很快就感到非常沮丧。我的堆叠条形图工作正常,但我无法让点击"事件"工作。

我在 GitHub 上找到了一条评论nnnick来自 Chart.js声明使用函数getBarsAtEvent,即使这个函数在图表.js文档中根本找不到(继续,搜索它(。该文档确实提到了chart引用的getElementsAtEvent函数,但这仅适用于折线图。

我在画布元素上设置了一个事件侦听器(正确的方式(:

canv.addEventListener('click', handleClick, false);

。然而,在我的handleClick函数中,chart.getBarsAtEvent是未定义的!

现在,在 Chart.js 文档中,有一条关于为条形图注册单击事件的不同方法的语句。这与 2 年前nnnick在 GitHub 上的评论有很大不同。

在全局图表默认值中,您可以为图表设置onClick函数。我在图表配置中添加了一个onClick函数,但它什么也没做......

那么,我到底如何让点击回调适用于我的条形图?!

任何帮助将不胜感激。谢谢!

PS:我没有使用GitHub的主版本。我试过了,但它一直在尖叫require is undefined,我还没有准备好包含 CommonJS,以便我可以使用此图表库。我宁愿自己写当图。相反,我下载并使用了直接从文档页面顶部的链接下载的标准构建版本。

示例:这是我正在使用的配置的示例:

var chart_config = {
    type: 'bar',
    data: {
        labels: ['One', 'Two', 'Three'],
        datasets: [
            {
                label: 'Dataset 1',
                backgroundColor: '#848484',
                data: [4, 2, 6]
            },
            {
                label: 'Dataset 2',
                backgroundColor: '#848484',
                data: [1, 6, 3]
            },
            {
                label: 'Dataset 3',
                backgroundColor: '#848484',
                data: [7, 5, 2]
            }
        ]
    },
    options: {
        title: {
            display: false,
            text: 'Stacked Bars'
        },
        tooltips: {
            mode: 'label'
        },
        responsive: true,
        maintainAspectRatio: false,
        scales: {
            xAxes: [
                {
                    stacked: true
                }
            ],
            yAxes: [
                {
                    stacked: true
                }
            ]
        },
        onClick: handleClick
    }
};

我设法通过查看图表.js源代码找到了问题的答案。

在 Chart.js 的第 3727 行,标准构建,是方法.getElementAtEvent。此方法向我返回单击的"图表元素"。此处有足够的数据来确定要在单击的数据集的向下钻取视图中显示哪些数据。

chart.getElementAtEvent 返回的数组的第一个索引上是一个值_datasetIndex 。此值显示单击的数据集的索引。

我相信,单击的特定栏由值_index表示。在我的问题示例中,_index会指出chart_config.data.labels中的One.

我的handleClick函数现在如下所示:

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

..其中chart是图表创建的图表的引用.js执行以下操作时:

chart = new Chart(canv, chart_config);

因此,通过单击选择的特定数据集可以如下:

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

你有它。我现在有一个数据点,我可以从中构建查询以显示单击的栏的数据。

八月7TH,2021。更新

现在有一种方法可以满足我们正在寻找的东西。看看这里

嗨,这是选项下的单击事件,它从 x 轴和 y 轴获取值

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);
}

我在 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)
  }
 }
}

你可以像这样使用 onClick。

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);
                    }
                }
            });

Chartjs V3.4.1

在查看了旧版本的解决方案后,这就是在 v3 中对我有用的方法:

const onClick = (event, clickedElements) => {
  if (clickedElements.length === 0) return
  const { dataIndex, raw } = clickedElements[0].element.$context
  const barLabel = event.chart.data.labels[dataIndex]
  ...
}
  • raw是点击的栏的值。
  • barLabel是单击的栏的标签。

您需要将onClick传递给条形图配置:

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

干得好!这似乎返回了正在绘制的数据值,在许多情况下,这些数据值可能会多次出现,因此不清楚点击了什么。

这将返回正在单击的栏的实际数据标签。我发现这在向下钻取到类别时更有用。

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

这显然是一个老问题,但它在我 2023 年搜索"图表.js点击事件"时评价很高。所以一个帖子似乎值得。

几乎没有任何发布的答案在 chart.js 的第 4 版中有效。对于版本 4,请使用 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>

图表.js团队在主要版本之间更改/破坏 API 的方式当然是大胆的,但它可能会无意中使历史堆栈溢出帖子(未指定图表.js版本(具有误导性和负面帮助值。我遇到的几乎所有图表.js社区帮助似乎都是这种情况。

我能够以另一种方式完成这项工作。可能不受支持,但有时,我发现标签和值都不足以为我提供必要的信息来填充钻取。

所以我所做的是为数据添加一组自定义属性:

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],

。等

然后,当我需要将钻取键推送到另一个 ajax 调用中时,我能够通过以下方式获取它:

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

所以我真的不确定将自定义元素添加到图表的数据中是否合适,但它到目前为止在 Chrome、IE 和 Firefox 中工作。我需要能够在钻取中放入比我真正想要显示的信息更多的信息。

完整内容的示例:https://wa.rrdsb.com/chartExamples

思潮?

我在多个数据集上遇到了同样的问题,并使用了以下解决方法:

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);
    }
}

假设您使用如下方法声明了一个图表:

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

声明 onclick 事件的一个好方法是侦听画布单击,如下所示:

({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") { ... }
}

图表的图表选项中.js v3.5.1 是最新的

检查以下示例代码

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
      }
    },
  };
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
});

检查下面的工作代码

<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>