Moving vertical line when hovering over the chart using chart.js
Asked Answered
B

3

30

I've been trying to add a vertical line that shows up with a tooltip when hovering over the chart. But I'm using chart.js 2.6 and the syntax from 1.x seems to be outdated.

I've the following code working for 1.x

var data = {
  labels: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"],
  datasets: [{
    data: [12, 3, 2, 1, 8, 8, 2, 2, 3, 5, 7, 1]
  }]
};

var ctx = document.getElementById("LineWithLine").getContext("2d");

Chart.types.Line.extend({
  name: "LineWithLine",
  initialize: function() {
    Chart.types.Line.prototype.initialize.apply(this, arguments);

    var originalShowTooltip = this.showTooltip;
    this.showTooltip = function(activePoints) {

      if (activePoints.length) {
        var ctx = this.chart.ctx;
        var scale = this.scale;
        ctx.save();
        ctx.strokeStyle = '#aaa';
        ctx.beginPath();
        ctx.moveTo(activePoints[0].x, scale.startPoint);
        ctx.lineTo(activePoints[0].x, scale.endPoint);
        ctx.stroke();
        ctx.restore();
      }

      return originalShowTooltip.apply(this, arguments);
    }
  }
});

new Chart(ctx).LineWithLine(data, {
  datasetFill: false,
  lineAtIndex: 2
});
<canvas id="LineWithLine"  style="width: 98vw; height:180px"></canvas>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.js"></script>

External link

Anyone know how to make it work for 2.6

Bedaub answered 18/7, 2017 at 7:9 Comment(0)
B
78

Solution for ChartJS 2.6.0

ꜱᴄʀɪᴘᴛ (ᴇxᴛᴇɴᴅɪɴɢ ʟɪɴᴇ ᴄʜᴀʀᴛ)

Chart.defaults.LineWithLine = Chart.defaults.line;
Chart.controllers.LineWithLine = Chart.controllers.line.extend({
    draw: function(ease) {
        Chart.controllers.line.prototype.draw.call(this, ease);

        if (this.chart.tooltip._active && this.chart.tooltip._active.length) {
            var activePoint = this.chart.tooltip._active[0],
                ctx = this.chart.ctx,
                x = activePoint.tooltipPosition().x,
                topY = this.chart.legend.bottom,
                bottomY = this.chart.chartArea.bottom;

            // draw line
            ctx.save();
            ctx.beginPath();
            ctx.moveTo(x, topY);
            ctx.lineTo(x, bottomY);
            ctx.lineWidth = 2;
            ctx.strokeStyle = '#07C';
            ctx.stroke();
            ctx.restore();
        }
    }
});

You would also need to set intersect: false for tooltips.

ᴡᴏʀᴋɪɴɢ ᴇxᴀᴍᴘʟᴇ ⧩

Chart.defaults.LineWithLine = Chart.defaults.line;
Chart.controllers.LineWithLine = Chart.controllers.line.extend({
   draw: function(ease) {
      Chart.controllers.line.prototype.draw.call(this, ease);

      if (this.chart.tooltip._active && this.chart.tooltip._active.length) {
         var activePoint = this.chart.tooltip._active[0],
             ctx = this.chart.ctx,
             x = activePoint.tooltipPosition().x,
             topY = this.chart.legend.bottom,
             bottomY = this.chart.chartArea.bottom;

         // draw line
         ctx.save();
         ctx.beginPath();
         ctx.moveTo(x, topY);
         ctx.lineTo(x, bottomY);
         ctx.lineWidth = 2;
         ctx.strokeStyle = '#07C';
         ctx.stroke();
         ctx.restore();
      }
   }
});

var chart = new Chart(ctx, {
   type: 'LineWithLine',
   data: {
      labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'],
      datasets: [{
         label: 'Statistics',
         data: [3, 1, 2, 5, 4, 7, 6],
         backgroundColor: 'rgba(0, 119, 204, 0.8)',
         borderColor: 'rgba(0, 119, 204, 0.3)',
         fill: false
      }]
   },
   options: {
      responsive: false,
      tooltips: {
         intersect: false
      },
      scales: {
         yAxes: [{
            ticks: {
               beginAtZero: true
            }
         }]
      }
   }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="ctx" height="200"></canvas>
Bonaventura answered 18/7, 2017 at 16:33 Comment(7)
Do you mind making it work on jsfiddle? Then I will approve it.Bedaub
Seems having problems if lines are stacked on Y axis.Nocturne
Amazing answer thanks. Copy and paste and it works perfectly. Colors/stroke weight easily customizable as well.Tailstock
@Bonaventura I was able to make this work, however, I tried to add: leftX = this.chart.scales['x-axis-0'].left, rightX = this.chart.scales['x-axis-0'].right; and ctx.moveTo(x, leftX); ctx.lineTo(x, rightX) But it won't draw a crosshair. : (Slavism
How come it shows the tooltip for the closest point by (x,y) distance, and not the closest vertical line by x-value? For example, if you hover over the top of the graph, the tooltip jumps from Jan to Apr.Angloirish
@Angloirish A setting was introuced in v2.7.0 that fixes this. You set axis: 'x' at options.tooltips.axis. Here is a fiddle that uses v2.8.0 and has that setting set: jsfiddle.net/hg5873zm/2 @Bonaventura If you can update the answer, I'd suggest you do so :)Marguerite
I changed x = activePoint.tooltipPosition().x to x = activePoint.element.x. I'm not sure if this is a chart.js 3 change or something else.Unarm
P
6

Try this:

var ctx = this.$refs.canvas.getContext("2d");
// new Chart(ctx, config);
var originalLineDraw = Chart.controllers.line.prototype.draw;
Chart.helpers.extend(Chart.controllers.line.prototype, {
draw: function() {
    originalLineDraw.apply(this, arguments);

    var chart = this.chart;
    var ctx = chart.chart.ctx;

if (this.chart.tooltip._active && this.chart.tooltip._active.length) {
    var activePoint = this.chart.tooltip._active[0];
    var ctx = this.chart.ctx;
    var x = activePoint.tooltipPosition().x;
    var topY = this.chart.scales['y-axis-0'].top;
    var bottomY = this.chart.scales['y-axis-0'].bottom;

    // draw line
    ctx.save();
    ctx.beginPath();
    ctx.moveTo(x, topY);
    ctx.lineTo(x, bottomY);
    ctx.lineWidth = 0.5;
    ctx.strokeStyle = '#eeeeee';
    ctx.stroke();
    ctx.restore();
}
}});

This will definitely help you.

Poling answered 21/8, 2019 at 15:12 Comment(0)
B
5

This question is five years old. Nowadays, we can achieve this using plugins and hook calls in this case beforeTooltipDraw to capture the tooltip.caretX. Also we can use the build-in interaction option to achieve this.

This implementacion works with versions 3.x and 4.0.1 of ChartJS

const $chart = document.getElementById('chart')

const plugin = {
    id: 'verticalLiner',
    afterInit: (chart, args, opts) => {
      chart.verticalLiner = {}
    },
    afterEvent: (chart, args, options) => {
        const {inChartArea} = args
        chart.verticalLiner = {draw: inChartArea}
    },
    beforeTooltipDraw: (chart, args, options) => {
        const {draw} = chart.verticalLiner
        if (!draw) return

        const {ctx} = chart
        const {top, bottom} = chart.chartArea
        const {tooltip} = args
        const x = tooltip?.caretX
        if (!x) return

        ctx.save()
        
        ctx.beginPath()
        ctx.moveTo(x, top)
        ctx.lineTo(x, bottom)
        ctx.stroke()
        
        ctx.restore()
    }
}

const data = {
  labels: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"],
  datasets: [{
    data: [12, 3, 2, 1, 8, 8, 2, 2, 3, 5, 7, 1]
  }]
}

const options = {
  type: 'line',
  data,
  options: {
    maintainAspectRatio: false,
    interaction: {
      mode: 'index',
      intersect: false,
    },
    plugins: {
      verticalLiner: {}
    }
  },
  plugins: [plugin]
}

const chart = new Chart($chart, options)
<div class="wrapper" style="width: 98vw; height:180px">
  <canvas id="chart"></canvas>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
Bulbar answered 16/11, 2022 at 10:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.