Chart.js:条形图点击事件

Posted

技术标签:

【中文标题】Chart.js:条形图点击事件【英文标题】:Chart.js: Bar Chart Click Events 【发布时间】:2016-09-04 11:37:56 【问题描述】:

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

我从 Chart.js 中找到了一个 comment on GitHub by nnnick 声明使用函数 getBarsAtEvent,即使在 Chart.js documentation 中根本找不到此函数(继续,搜索它)。文档确实提到了chart 参考的getElementsAtEvent 函数,但这仅适用于折线图。

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

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

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

现在,在 Chart.js 文档中,有一条关于为条形图注册点击事件的不同方法的声明。和2年前nnnick在GitHub上的评论大不相同。

在Global Chart Defaults 中,您可以为图表设置onClick 函数。我在图表配置中添加了一个onClick 函数,但它什么也没做...

那么,我到底如何让点击回调为我的条形图工作?!

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

P.S.:我没有使用来自 GitHub 的主版本。我试过了,但它一直在尖叫require is undefined,而且我还没有准备好包含 CommonJS 以便我可以使用这个图表库。我宁愿写自己的dang chart。相反,我下载并使用了我直接从documentation page 顶部的链接下载的Standard Build 版本。

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

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
    
;

【问题讨论】:

我发现我可以通过设置chart.config.options.onClick = handleClick 来调用我的onClick 函数,但这并不能帮助我找到点击了图表的哪一部分。这基本上与我自己在canvas 节点上设置我的事件监听器相同。 几年后,现在有了这个,给那些搜索并找到这个问题的人:chartjs.org/docs/latest/general/interactions/events.html 它提到activeElementsoptions 参数中作为onClick 的第二个参数传递给new Chart() @tscizzle 您的链接已损坏 链接其实是这个:chartjs.org/docs/2.8.0/general/interactions/events.html 【参考方案1】:

通过查看Chart.js source code,我设法找到了我的问题的答案。

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

chart.getElementAtEvent 返回的数组的第一个索引是一个值_datasetIndex。该值显示被点击的数据集的索引。

我相信,被点击的特定栏由值_index 标注。在我的问题示例中,_index 将指向One 中的chart_config.data.labels

我的handleClick 函数现在看起来像这样:

function handleClick(evt)

    var activeElement = chart.getElementAtEvent(evt);

..其中chart是chart.js在做时创建的图表的引用:

chart = new Chart(canv, chart_config);

因此可以找到通过点击选择的特定数据集:

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

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

2021 年 8 月 7 日。更新

现在我们正在寻找一种方法。看看here

【讨论】:

这也是我在文档中搜索数小时后发现的。但我的问题是,它返回了我所有的数据集,所以activeElement[0]._datasetIndex 仍然是相同的 - 零,无论我点击哪个栏! _index 正在工作。你没有遇到同样的问题吗? 我没有遇到过这个问题@JánJanočko,但是自从这篇文章之后我就没有使用过 Chart.js。我需要再次查看它,因为我可以看到我创建的图表小部件存在一些问题。我会及时通知你我的发现。 感谢@WebWanderer。获得那个 activeElement 是关键。就我而言,我可以只使用图表的label,然后在其上执行switch()。然后每个case "<myLabel>": 都会做某件事,对我来说只需转到不同的网页:window.location.href = "/myLabelPage"; 嗨@JánJanočko,无论我点击哪个栏,我的 activeElement[0]._datasetIndex 也为零。你有没有找到一种方法来获得准确的点击栏值? 这已记录在 API 页面中! chartjs.org/docs/latest/developers/api.html#getelementatevente【参考方案2】:

您好,这是选项下的点击事件,它从 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);

【讨论】:

我收到一个错误,因为无法读取 i 未定义的属性“0” 请发帖 简洁明了。为我工作! 这应该是公认的答案!因此,您只需将其添加到options:events: ['click'], onClick: function(c, i) ...,该函数将被自动调用。只需为e 未定义添加一个验证,否则如果用户单击距离条太远,它将引发异常。 @mikelowry,我试过很多次了。我不知道 c 是什么,并且 c 没有在该函数中使用。但如果没有 c,该功能将无法工作。所以我就把它留在那里了。【参考方案3】:

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

【讨论】:

【参考方案4】:

你可以像这样使用 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);
                    
                
            );

【讨论】:

请解释一下 onClick 函数中的代码,因为它不清楚您建议解决问题的方法。【参考方案5】:

干得好!不过,这似乎返回了图表中的数据值,在许多情况下可能会出现不止一次,因此不清楚点击了什么。

这将返回被点击的栏的实际数据标签。我发现这在深入研究类别时更有用。

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

【讨论】:

如果您没有标签,这将不起作用。您可以以data: [t:"2020-01-04 08:06Z", y:0,t:"2020-01-04 08:07Z", y:45,t:"2020-01-04 08:08Z", y:47] 格式指定数据,在这种情况下,标签将不存在。【参考方案6】:

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

【讨论】:

【参考方案7】:

我能够以另一种方式完成这项工作。 可能不受支持,但有时,我发现标签和值都不足以让我获得填充钻取所需的信息。

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

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

想法?

【讨论】:

移除getContext。 var ctx = document.getElementById("cnvMyChart").getContext("2d");试试var ctx = document.getElementById("cnvMyChart");【参考方案8】:

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

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

【讨论】:

好技巧!非常适合堆叠组图。谢谢! 如果用户点击x轴标签,那么如何获取标签?【参考方案9】:

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

  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")  ... 

【讨论】:

【参考方案10】:

在最新的 Chart.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
      
    ,

  ;

【讨论】:

【参考方案11】:
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
);

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。 虽然这段代码 sn-p 可以解决问题,但它没有解释为什么或如何回答这个问题。请include an explanation for your code,因为这确实有助于提高您的帖子质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。

以上是关于Chart.js:条形图点击事件的主要内容,如果未能解决你的问题,请参考以下文章

Chart.js:在图例点击时刷新第二个Y轴步骤

iOS 中 Fusioncharts 上的点击事件

onClick 事件隐藏数据集 Chart.js V2

每个 dc.js 图表的点击事件

分组条形图,在 chart.js 中

如何在chart.js中为条形图绘制基线