2

I have a pie chart that needs to call one function when you click on a segment of the chart and another different one if a label in the legend is clicked. I hoped to achieve this behaviour with the following:

options: {
            responsive: true,
            legend: {
                position: 'right',
                onClick: function (event, elem) {
                    graph_legend_click(elem.text);
                },
            },
            onClick: function (event) {
                graph_click( event);
            }
        }

However in practice, only the second onclick (which calls graph_click( event);) will actually get executed. the legend onClick doesnt work. What can i do to prevent the second onClick from overwriting the first?

Notaras
  • 611
  • 1
  • 14
  • 44
  • Your code works for me as you have described, working example: https://jsfiddle.net/vanmkLc0 – Lukas Bach Sep 21 '18 at 01:18
  • @LukasBach you both seem to haven't read the question, which reads `pie`. – Martin Zeitler Sep 21 '18 at 01:51
  • @Notaras actually both event are executed not sure what is different in your case: http://jsfiddle.net/m0kyL3hc/3/ – Dipen Shah Sep 23 '18 at 08:25
  • @Notaras could you please have a look at the answers once and clarify, what exactly you've meant with "being overwritten" ...because it is difficult to reproduce that behavior. – Martin Zeitler Sep 24 '18 at 00:33
  • @Martin Zeitler I should have rephrased but at the end of the day, i dont want to click on the legend and have both onclick events execute. Ie if legend is clicked, call `graph_legend_click` else if graph is clicked, call `graph_click()`. As noted above, both onclicks are executed – Notaras Sep 24 '18 at 01:40
  • @MartinZeitler Your suggestion of isolating the click event coordinates is a good one. I will try it and get back to you – Notaras Sep 24 '18 at 01:42

3 Answers3

5

one can only differ the click-event by their coordinates ...where the conditions are:

  • event.clientY >= chart.boxes[0].height, when the legend is on top.

  • event.clientX <= chart.boxes[0].left, when the legend is to the right.

similar conditions could also be used, when the legend is to the left or at the bottom.

var chart;
var config = {
    type: 'pie',
    data: {
        datasets: [{
            data: [
                Math.round(Math.random() * 100),
                Math.round(Math.random() * 100),
                Math.round(Math.random() * 100),
                Math.round(Math.random() * 100),
                Math.round(Math.random() * 100)
            ],
            backgroundColor: [
                "rgb(255, 99, 132)",
                "rgb(255, 159, 64)",
                "rgb(255, 205, 86)",
                "rgb(75, 192, 192)",
                "rgb(54, 162, 235)"
            ],
            label: 'Dataset 1'
        }],
        labels: [
            'Red',
            'Orange',
            'Yellow',
            'Green',
            'Blue'
        ]
    },
    options: {
        responsive: true,
        onClick: function (event) {
            
            /* checking where the click actually happens */
            var box = chart.boxes[0];
            if((box.position === "top" && event.clientY >= box.height) || (box.position === "right" && event.clientX <= box.left)) {
                console.log("chart click  @ x: " + event.clientX + ", y:" + event.clientY);
            }
        },
        legend: {
            position: 'top',
            onClick: function (event, elem) {
                console.log("legend click @ x: " + event.clientX + ", y:" + event.clientY);
            }
        }
    }
};

$(function() {
  chart = new Chart($('#chart-area')[0].getContext('2d'), config);
});
html, body {height: 100%; width: 100%; margin: 0;}
canvas#chart-area {height: 100px; width: 100px;}
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<div id="canvas-holder"><canvas id="chart-area"></canvas></div>
Martin Zeitler
  • 1
  • 19
  • 155
  • 216
2

The click on the legend seems to be blocked off by the hidden iframe, that chartjs is creating to detect resize events. You can set the chartjs instance to unresponsive if it is not important to you that the chart automatically resizes.

var options = {
  type: 'pie',
  data: {
    labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    datasets: [{
      label: '# of Votes',
      data: [12, 19, 3, 5, 2, 3],
      borderWidth: 1
    }]
  },
  options: {
    responsive: false,
    onClick: function(event) {
      alert("onClickGraph");
    },
    legend: {
      onClick: function(event) {
        alert("onClickLegend");
      }
    }
  }
}

var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
canvas {
  background-color: #eee;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.js"></script>

<body>
  <canvas id="chartJSContainer" width="600" height="400"></canvas>
</body>

You can also make the parent container relatively positioned, which will also allow the chartjs instance to be responsive even though the flag "responsive" is set to false and therefore the iframe is not displayed: http://www.chartjs.org/docs/latest/general/responsive.html#important-note

Lukas Bach
  • 3,559
  • 2
  • 27
  • 31
  • The question was, why the legend onclick was not being triggered. It is intentional design of chartjs that legend onclick triggers both events. If this is a problem, you can just add additional code to check if both events occured, and in this case ignore the graph onclick event. – Lukas Bach Sep 24 '18 at 00:16
  • well, I could not reproduce the behavior, that one event would prevent or overwrite the other (depending on, what these functions might have performed, it might have appeared alike if they would)... while when configuring on legend click, the chart is not being filtered - as it ordinary is (it's unclear to me, if not this was meant). – Martin Zeitler Sep 24 '18 at 00:21
1

If you add the following condition it will work. It seems if you return from the function, it will execute the legend one

options: {
            responsive: true,
            legend: {
                position: 'right',
                onClick: function (event, elem) {
                    graph_legend_click(elem.text);
                },
            },
            onClick: function (event, item) {
                if(item.length == 0)
                   return;

                graph_click( event);
            }
        }

this also works

var chart = new Chart(ctx, config);

chart.options.legend.onClick = function(ev, legendItem){
     alert(legenItem.text);
}
chart.options.onClick = function(ev, item){
     if (item.length == 0)
        return;
     // TODO
}
Alireza
  • 126
  • 1
  • 6