5

I'm using chart.js 2.0 beta2 and have several line charts on a page and a slider. I'd like to highlight the data point on each line chart that matches the slider position (they all have the same number of points). I can't figure out how to easily make a point active in the code. Thanks for any tips.

Brad
  • 823
  • 1
  • 9
  • 17

3 Answers3

3

SOLUTION FOR 2.0 BETA

Extend the chart controller of your choice, and fire off a simulated click event to show the tooltip. Here is some code that works for 2.0 (here is a fiddle):

var data = {
    labels: ["January", "February", "March", "April", "May", "June", "July"],
    datasets: [
        {
            label: "My First dataset",
            fill: false,
            backgroundColor: "rgba(220,220,220,0.2)",
            borderColor: "rgba(220,220,220,1)",
            borderCapStyle: 'butt',
            borderDash: [],
            borderDashOffset: 0.0,
            borderJoinStyle: 'miter',
            pointBorderColor: "rgba(220,220,220,1)",
            pointBackgroundColor: "#fff",
            pointBorderWidth: 1,
            pointHoverRadius: 5,
            pointHoverBackgroundColor: "rgba(220,220,220,1)",
            pointHoverBorderColor: "rgba(220,220,220,1)",
            pointHoverBorderWidth: 2,
            tension: 0.1,
            data: [65, 59, 80, 81, 56, 55, 40]
        },
        {
            label: "My Second dataset",
            fill: true,
            backgroundColor: "rgba(220,220,220,0.2)",
            borderColor: "rgba(220,220,220,1)",
            pointBorderColor: "rgba(220,220,220,1)",
            pointBackgroundColor: "#fff",
            pointBorderWidth: 1,
            pointHoverRadius: 5,
            pointHoverBackgroundColor: "rgba(220,220,220,1)",
            pointHoverBorderColor: "rgba(220,220,220,1)",
            pointHoverBorderWidth: 2,
            data: [28, 48, 40, 19, 86, 27, 90]
        }
    ]
};

var options = {
    responsive: false
};

Chart.helpers.extend(Chart.controllers.line.prototype, {
    fireSliderEvent: function(point, canvas, boundingRect){
        var mouseX = Math.round((boundingRect.left + point._view.x) / (boundingRect.right - boundingRect.left) * canvas.width / this.chart.chart.currentDevicePixelRatio);
        var mouseY = Math.round((boundingRect.top + point._view.y) / (boundingRect.bottom - boundingRect.top) * canvas.height / this.chart.chart.currentDevicePixelRatio);
        var oEvent = document.createEvent('MouseEvents');
        oEvent.initMouseEvent('click', true, true, document.defaultView,
            0, mouseX, mouseY, mouseX, mouseY,
            false, false, false, false, 0, canvas);
        canvas.dispatchEvent(oEvent);
    },
    highlightPoints: function(point){
        var canvas = this.chart.chart.canvas;
        var boundingRect = canvas.getBoundingClientRect();
        var points = this.getDataset().metaData;
        this.fireSliderEvent(points[point], canvas, boundingRect);
    }
});

var ctx = $("#canvas");
var myLine = new Chart(ctx, {
    type: 'line',
    data: data,
    options: options
});

var highlight = function(dataset, point){
    myLine.data.datasets[dataset].controller.highlightPoints(point);
}

$("#slider").slider({
    max: myLine.data.datasets[0].data.length-1,
    slide: function( event, ui ) { highlight(0, ui.value); }
});
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.0.0-beta/Chart.js"></script>

<div id="slider" style="width: 500px;"></div>
<canvas id="canvas" height="300" width="500"></canvas>
JstnPwll
  • 8,585
  • 2
  • 33
  • 56
  • That's pretty cool. I wish the chart would just support a set by index method natively but I guess that's the nature of a canvas based chart. – Brad Jan 08 '16 at 23:34
2

SOLUTION FOR 1.x

You should extend the chart type and add your own method to select the point. Here is some code that will show the tooltip for each point depending on slider position:

var lineChartData = {
    "datasets": [{
        "data": [
            "85",
            "87",
            "70",
            "80",
            "78",
            "69",
            "150",
            "93",
            "59",
            "88"],
            "pointStrokeColor": "#fff",
            "fillColor": "rgba(220,220,220,0.5)",
            "pointColor": "rgba(220,220,220,1)",
            "strokeColor": "rgba(220,220,220,1)"
    }],
        "labels": [
        "2013-01-01",
        "2013-01-04",
        "2013-01-15",
        "2013-02-03",
        "2013-03-25",
        "2013-04-03",
        "2013-04-14",
        "2013-05-27",
        "2013-05-27",
        "2013-08-03"],
};

var options = {showTooltips: false};

Chart.types.Line.extend({
    name: "LineAlt",
    highlightPoints: function(datasetIndex, pointIndexArray){
        var activePoints = [];
        var points = this.datasets[datasetIndex].points;
        for(i in pointIndexArray){
            if(points[pointIndexArray[i]]){
            activePoints.push(points[pointIndexArray[i]]);
          }
        }
        this.showTooltip(activePoints);
    }
});

var myLine = new Chart(document.getElementById("canvas").getContext("2d")).LineAlt(lineChartData, options);

var highlight = function(index){
    myLine.highlightPoints(0, [index]);
}

$("#slider").slider({
  max: lineChartData.datasets[0].data.length-1,
  slide: function( event, ui ) { highlight(ui.value); },
});
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>

<div id="slider" style="width: 500px;"></div>
<canvas id="canvas" height="300" width="500"></canvas>
LeeLenalee
  • 27,463
  • 6
  • 45
  • 69
JstnPwll
  • 8,585
  • 2
  • 33
  • 56
  • This is great, thank you so much. I'm not able to get the extends to work in the 2.0 beta though. Seems like the construction method has changed ? – Brad Jan 08 '16 at 15:36
  • 1
    Doesn't look like showTooltip() is valid anymore in 2.0 as well. – Brad Jan 08 '16 at 19:17
  • Maybe you could use the older version? :D I don't really have time to dive into the source code of 2.0, it looks like they changed things up a lot. – JstnPwll Jan 08 '16 at 21:24
  • I need features in 2.0 but I appreciate you posting your solution for the 1.x version. 2.0 seems entirely different so I'm hoping one of the contributors will pick this up. – Brad Jan 08 '16 at 22:14
0

First BIG THANKS to JstnPwll for amazing answer that helped me a lot!

The ChartJS API has changed a lot and 2.0 BETA does not work I needed this in ReactJS and the complexity of "extend" coordinator was beyond my competence

This simpler works for Chart.JS 2.9 In original point is sometimes number sometimes object So I made explicit using suffix num to clarify usage Simply call fireSliderEvent(2,5);

fireSliderEvent = (datasetnum,pointnum)=>{

  let myLine=this.chartRef.current.chartInstance;
  //console.log(JSON.stringify(JSON.decycle(this.chartRef.current),null,2));

  var canvas = myLine.canvas;
  var boundingRect = canvas.getBoundingClientRect();

  var meta = myLine.getDatasetMeta(datasetnum);

  // https://stackoverflow.com/questions/6157929/how-to-simulate-a-mouse-click-using-javascript
  var mouseX = Math.round((boundingRect.left + meta.dataset._children[pointnum]._view.x) / (boundingRect.right - boundingRect.left) * canvas.width / myLine.currentDevicePixelRatio);
  
  var mouseY = Math.round((boundingRect.top + meta.dataset._children[pointnum]._view.y) / (boundingRect.bottom - boundingRect.top) * canvas.height / myLine.currentDevicePixelRatio);

  var oEvent = document.createEvent('MouseEvents');

  oEvent.initMouseEvent('click', true, true, document.defaultView,
         0, mouseX, mouseY, mouseX, mouseY,
         false, false, false, false, 0, canvas);

  canvas.dispatchEvent(oEvent);
} 
Jayanta
  • 135
  • 4