score:4

Accepted answer

You can just add a second draw block for the y coordinate that you get from the tooltip, first you move to the left of the chartArea that you can get the same way you got bottom and top and then you move to the right on the same Y

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,
        y = activePoint.tooltipPosition().y,
        topY = this.chart.legend.bottom,
        bottomY = this.chart.chartArea.bottom,
        left = this.chart.chartArea.left,
        right = this.chart.chartArea.right;


      // Set line opts
      ctx.save();
      ctx.lineWidth = 1;
      ctx.setLineDash([3, 3]);
      ctx.strokeStyle = '#FF4949';

      // draw vertical line      
      ctx.beginPath();
      ctx.moveTo(x, topY);
      ctx.lineTo(x, bottomY);
      ctx.stroke();

      // Draw horizontal line
      ctx.beginPath();
      ctx.moveTo(left, y);
      ctx.lineTo(right, y);
      ctx.stroke();

      ctx.restore();
    }
  }
});

var options = {
  type: 'LineWithLine',
  data: {
    labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    datasets: [{
        label: '# of Votes',
        data: [12, 19, 3, 5, 2, 3],
        borderWidth: 1
      },
      {
        label: '# of Points',
        data: [7, 11, 5, 8, 3, 7],
        borderWidth: 1
      }
    ]
  },
  options: {
  }
}

var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
  <canvas id="chartJSContainer" width="600" height="400"></canvas>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script>
</body>

Edit:

You should use a custom plugin for this since you dont draw everytime you move the cursor and you can enforce this by using a custom plugin:

const options = {
  type: 'line',
  data: {
    labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    datasets: [{
        label: '# of Votes',
        data: [12, 19, 3, 5, 2, 3],
        borderWidth: 1
      },
      {
        label: '# of Points',
        data: [7, 11, 5, 8, 3, 7],
        borderWidth: 1
      }
    ]
  },
  options: {
    plugins: {
      corsair: {
        dash: [2, 2],
        color: 'red',
        width: 3
      }
    }
  },
  plugins: [{
    id: 'corsair',
    afterInit: (chart) => {
      chart.corsair = {
        x: 0,
        y: 0
      }
    },
    afterEvent: (chart, evt) => {
      const {
        chartArea: {
          top,
          bottom,
          left,
          right
        }
      } = chart;
      const {
        x,
        y
      } = evt;
      if (x < left || x > right || y < top || y > bottom) {
        chart.corsair = {
          x,
          y,
          draw: false
        }
        chart.draw();
        return;
      }

      chart.corsair = {
        x,
        y,
        draw: true
      }

      chart.draw();
    },
    afterDatasetsDraw: (chart, _, opts) => {
      const {
        ctx,
        chartArea: {
          top,
          bottom,
          left,
          right
        }
      } = chart;
      const {
        x,
        y,
        draw
      } = chart.corsair;

      if (!draw) {
        return;
      }

      ctx.lineWidth = opts.width || 0;
      ctx.setLineDash(opts.dash || []);
      ctx.strokeStyle = opts.color || 'black'

      ctx.save();
      ctx.beginPath();
      ctx.moveTo(x, bottom);
      ctx.lineTo(x, top);
      ctx.moveTo(left, y);
      ctx.lineTo(right, y);
      ctx.stroke();
      ctx.restore();
    }
  }]
}

const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
  <canvas id="chartJSContainer" width="600" height="400"></canvas>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script>
</body>

Edit:

Updated answer for v3

const options = {
  type: 'line',
  data: {
    labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    datasets: [{
        label: '# of Votes',
        data: [12, 19, 3, 5, 2, 3],
        borderWidth: 1
      },
      {
        label: '# of Points',
        data: [7, 11, 5, 8, 3, 7],
        borderWidth: 1
      }
    ]
  },
  options: {
    plugins: {
      corsair: {
        dash: [2, 2],
        color: 'red',
        width: 3
      }
    }
  },
  plugins: [{
    id: 'corsair',
    afterInit: (chart) => {
      chart.corsair = {
        x: 0,
        y: 0
      }
    },
    afterEvent: (chart, evt) => {
      const {
        chartArea: {
          top,
          bottom,
          left,
          right
        }
      } = chart;
      const {
        event: {
          x,
          y
        }
      } = evt;
      if (x < left || x > right || y < top || y > bottom) {
        chart.corsair = {
          x,
          y,
          draw: false
        }
        chart.draw();
        return;
      }

      chart.corsair = {
        x,
        y,
        draw: true
      }

      chart.draw();
    },
    afterDatasetsDraw: (chart, _, opts) => {
      const {
        ctx,
        chartArea: {
          top,
          bottom,
          left,
          right
        }
      } = chart;
      const {
        x,
        y,
        draw
      } = chart.corsair;

      if (!draw) {
        return;
      }

      ctx.lineWidth = opts.width || 0;
      ctx.setLineDash(opts.dash || []);
      ctx.strokeStyle = opts.color || 'black'

      ctx.save();
      ctx.beginPath();
      ctx.moveTo(x, bottom);
      ctx.lineTo(x, top);
      ctx.moveTo(left, y);
      ctx.lineTo(right, y);
      ctx.stroke();
      ctx.restore();
    }
  }]
}

const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
  <canvas id="chartJSContainer" width="600" height="400"></canvas>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script>
</body>

score:2

I have done exactly this (but vertical line only) in a previous version of one of my projects. Unfortunately this feature has been removed but the older source code file can still be accessed via my github.

The key is this section of the code:

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 = 0.5;
         ctx.strokeStyle = '#A6A6A6';
         ctx.stroke();
         ctx.restore();
      }
   }
});

Another caveat is that the above code works with Chart.js 2.8 and I am aware that the current version of Chart.js is 3.1. I haven't read the official manual on the update but my personal experience is that this update is not 100% backward-compatible--so not sure if it still works if you need Chart.js 3. (But sure you may try 2.8 first and if it works you can then somehow tweak the code to make it work on 3.1)


Related Query

More Query from same tag