如何在Chart.js v2.0中的标签上添加OnClick事件

How to add OnClick Event on labels in Chart.js v2.0?

本文关键字:标签 添加 事件 OnClick Chart js v2      更新时间:2023-09-26

在chartjs 2.0中寻找在"label"元素上添加onClick句柄的方法由于使用以下方法,每当单击Char.js V2.0 RadarChart中的任何一个标签属性时,都会在console.log中返回"未定义"。

var data = {
    // below line is the labels 
    labels: ["Eating", "Drinking", "Sleeping", "Designing", "Coding", "Cycling", "Running"],
    datasets: [
        {
            label: "My First dataset", //this only shows as legend, not label.
            backgroundColor: "rgba(179,181,198,0.2)",
            borderColor: "rgba(179,181,198,1)",
            pointBackgroundColor: "rgba(179,181,198,1)",
            pointBorderColor: "#fff",
            pointHoverBackgroundColor: "#fff",
            pointHoverBorderColor: "rgba(179,181,198,1)",
            data: [65, 59, 90, 81, 56, 55, 40]
        },
       ....

//Below is how to OnClick on chart points in chart.js V2, 
//However, it didn't apply to labels, will return "undifined" .   
$('#ChartV2').click(function(e) {
                var activePoints = myRadarChart.getElementsAtEvent(e);                  
                var firstPoint = activePoints[0];
                console.log(firstPoint);
                if (firstPoint !== undefined){
                   alert(firstPoint._index);                        
                }
 });

在chart.js 2.5(可能更早)中,您可以在选项中单击onClick:

'legend' : {
    'onClick' : function (evt, item) {
                    console.log ('legend onClick', evt, item);
                },
    'display' : true,
    'labels' : ...

getElementsAtEvent只检查图表的main元素(条形图、点、扇区…)。如果您也想考虑标签,则必须重新实现标签的功能。

您需要的大部分代码已经在Chart.js库代码中的不同方法中提供。只需按照以下步骤复制粘贴/清理即可。


脚本

你的点击手应该是

$('#myChart').click(function (e) {
    var helpers = Chart.helpers;
    var eventPosition = helpers.getRelativePosition(e, myRadarChart.chart);
    var mouseX = eventPosition.x;
    var mouseY = eventPosition.y;
    var activePoints = [];
    // loop through all the labels
    helpers.each(myRadarChart.scale.ticks, function (label, index) {
        for (var i = this.getValueCount() - 1; i >= 0; i--) {
            // here we effectively get the bounding box for each label
            var pointLabelPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.options.reverse ? this.min : this.max) + 5);
            var pointLabelFontSize = helpers.getValueOrDefault(this.options.pointLabels.fontSize, Chart.defaults.global.defaultFontSize);
            var pointLabeFontStyle = helpers.getValueOrDefault(this.options.pointLabels.fontStyle, Chart.defaults.global.defaultFontStyle);
            var pointLabeFontFamily = helpers.getValueOrDefault(this.options.pointLabels.fontFamily, Chart.defaults.global.defaultFontFamily);
            var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily);
            ctx.font = pointLabeFont;
            var labelsCount = this.pointLabels.length,
                halfLabelsCount = this.pointLabels.length / 2,
                quarterLabelsCount = halfLabelsCount / 2,
                upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
                exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
            var width = ctx.measureText(this.pointLabels[i]).width;
            var height = pointLabelFontSize;
            var x, y;
            if (i === 0 || i === halfLabelsCount)
                x = pointLabelPosition.x - width / 2;
            else if (i < halfLabelsCount)
                x = pointLabelPosition.x;
            else
                x = pointLabelPosition.x - width;
            if (exactQuarter)
                y = pointLabelPosition.y - height / 2;
            else if (upperHalf)
                y = pointLabelPosition.y - height;
            else
                y = pointLabelPosition.y
            // check if the click was within the bounding box
            if ((mouseY >= y && mouseY <= y + height) && (mouseX >= x && mouseX <= x + width))
                activePoints.push({ index: i, label: this.pointLabels[i] });
        }
    }, myRadarChart.scale);
    var firstPoint = activePoints[0];
    if (firstPoint !== undefined) {
        alert(firstPoint.index + ': ' + firstPoint.label);
    }
});

Fiddle-http://jsfiddle.net/1Lngmtz7/

我为2.8.0版本提出了一个解决方案,将RadialLinear刻度的标签位置计算复制到事件处理程序中。

document.getElementById("myChart").onclick = function (e) {
    var helpers = Chart.helpers;
    var scale = myRadarChart.scale;
    var opts = scale.options;
    var tickOpts = opts.ticks;
    // Position of click relative to canvas.
    var mouseX = e.offsetX;
    var mouseY = e.offsetY;
    var labelPadding = 5; // number pixels to expand label bounding box by
    // get the label render position
    // calcs taken from drawPointLabels() in scale.radialLinear.js
    var tickBackdropHeight = (tickOpts.display && opts.display) ?
        helpers.valueOrDefault(tickOpts.fontSize, Chart.defaults.global.defaultFontSize)
        + 5: 0;
    var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
    for (var i = 0; i < scale.pointLabels.length; i++) {
        // Extra spacing for top value due to axis labels
        var extra = (i === 0 ? tickBackdropHeight / 2 : 0);
        var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5);
        // get label size info.
        // TODO fix width=0 calc in Brave?
        // https://github.com/brave/brave-browser/issues/1738
        var plSize = scale._pointLabelSizes[i];
        // get label textAlign info
        var angleRadians = scale.getIndexAngle(i);
        var angle = helpers.toDegrees(angleRadians);
        var textAlign = 'right';
        if (angle == 0 || angle == 180) {
            textAlign = 'center';
        } else if (angle < 180) {
            textAlign = 'left';
        }
        // get label vertical offset info
        // also from drawPointLabels() calcs
        var verticalTextOffset = 0;
        if (angle === 90 || angle === 270) {
            verticalTextOffset = plSize.h / 2;
        } else if (angle > 270 || angle < 90) {
            verticalTextOffset = plSize.h;
        }
        // Calculate bounding box based on textAlign
        var labelTop = pointLabelPosition.y - verticalTextOffset - labelPadding;
        var labelHeight = 2*labelPadding + plSize.h;
        var labelBottom = labelTop + labelHeight;
        var labelWidth = plSize.w + 2*labelPadding;
        var labelLeft;
        switch (textAlign) {
        case 'center':
          var labelLeft = pointLabelPosition.x - labelWidth/2;
          break;
        case 'left':
          var labelLeft = pointLabelPosition.x - labelPadding;
          break;
        case 'right':
          var labelLeft = pointLabelPosition.x - labelWidth + labelPadding;
          break;
        default:
          console.log('ERROR: unknown textAlign '+textAlign);
        }
        var labelRight = labelLeft + labelWidth;
        // Render a rectangle for testing purposes
        ctx.save();
        ctx.strokeStyle = 'red';
        ctx.lineWidth = 1;
        ctx.strokeRect(labelLeft, labelTop, labelWidth, labelHeight);
        ctx.restore();
        // compare to the current click
        if (mouseX >= labelLeft && mouseX <= labelRight && mouseY <= labelBottom && mouseY >= labelTop) {
            alert(scale.pointLabels[i]+' clicked');
            // Break loop to prevent multiple clicks, if they overlap we take the first one.
            break;
        }
    }
};

JSFiddle在这里:

https://jsfiddle.net/simoncoggins/7r08uLk9/

这种方法的缺点是,如果未来核心标签的实施方式发生变化,它将被打破。如果库将标签位置的计算与其呈现分离,并开始通过API公开位置信息,那会更好。然后,这个解决方案可以大大简化,并且对库的更改更加健壮。

我在这里开了一张票:

https://github.com/chartjs/Chart.js/issues/6549

如果这个问题对你有用,请评论一下。

无法读取未定义的Chartjs 的属性"getDatasetMeta"

如果你从chartjs的doc示例中得到这个错误,那么客户端就是问题所在。将其替换为当前实例的客户端。

在我的情况下,我使用myHorizontalBar

var defaultLegendClickHandler = function(e, legendItem) {
    var index = legendItem.datasetIndex;
    var ci = window.myHorizontalBar; //this.chart;
    var meta = ci.getDatasetMeta(index);
    // See controller.isDatasetVisible comment
    meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;
    // We hid a dataset ... rerender the chart
    ci.update();
}  

myHorizontalBar

        window.onload = function() {
        var ctx = document.getElementById('canvas').getContext('2d');
        window.myHorizontalBar = new Chart(ctx, {
            type: 'horizontalBar',
            data: horizontalBarChartData,
            options: {