web-dev-qa-db-ja.com

Chart.js v2.0のラベルにOnClickイベントを追加するにはどうすればよいですか?

Chartjs 2.0の「label」要素にonClickハンドルを追加するメソッドを探しています。以下を使用すると、Char.js V2.0RadarChartのラベル属性のいずれかをクリックするたびにconsole.logに「undifined」が返されます。

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

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

フィドル- http://jsfiddle.net/1Lngmtz7/

8
potatopeelings

chart.js 2.5(おそらくそれ以前)では、オプションにonClickを入れることができます:

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

ラベル位置の計算をRadialLinearスケールからイベントハンドラーにコピーすることで、バージョン2.8.0のこれに対する解決策を思いつきました。

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

それがあなたに役立つならば、その問題についてコメントしてください。

1
simoncoggins

未定義のChartjsのプロパティ 'getDatasetMeta'を読み取ることができません

chartjsのドキュメント例 からこのエラーが発生した場合は、クライアントに問題があります。現在のインスタンスのクライアントに置き換えます。

私の場合、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();
}  

myHorizo​​ntalBar

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