Here is a piece of code I developed for one of my projects where I needed to customize ChartJS with vertical line on hovered points and custom tooltip.
View DEMO
Open demo in a full window – https://demos.webdevpuneet.com/chartjs-verticle-line/index.html
CODE
<!doctype html>
<html lang="en">
<head>
<!-- Meta Tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="INDEX,FOLLOW"/>
<title>Site Title</title>
<meta name="description" content="Site Description"/>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700,800" rel="stylesheet">
<!-- Scripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>
<!-- Framework + Styles -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.css">
<link rel="stylesheet" href="./assets/css/page.css">
</head>
<body class="page-home">
<div class="container">
<section class="bink-investgraph">
<div class="bink-investgraph__title">
<a style="color: #000;text-decoration:none;" href="https://webdevpuneet.com"><img src="./assets/img/bink-img4.png"> ChartJS – Vertical line on points and custom tooltip</a>
</div>
<canvas id="ctx"></canvas>
<script>
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 = activePoint.tooltipPosition().y - 20,
bottomY = this.chart.scales['y-axis-0'].bottom;
// draw line
ctx.save();
ctx.beginPath();
ctx.moveTo(x-5 , topY);
ctx.lineTo(x, bottomY);
ctx.lineWidth = 10;
ctx.strokeStyle = '#ff0000';
ctx.stroke();
ctx.restore();
// draw Circle
ctx.save();
ctx.beginPath();
ctx.arc(x-5.5 , topY + 5, 7.5, 0, 2 * Math.PI);
ctx.fillStyle ="#ff0000";
ctx.fill();
ctx.stroke();
}
}
});
var chart = new Chart(ctx, {
type: 'LineWithLine',
data: {
labels: ['20-JUN-1990', '21-JUN-1990', '25-JUN-1990', '26-JUN-1990', '21-JUL-1990', '22-AUG-1990', '25-SEP-1990', '20-OCT-1990', '21-NOV-1990', '21-DEC-1990', '1-JAN-1991'],
datasets: [{
data: [80, 70, 90, 70, 90, 40, 30, 70, 80, 90, 70],
label: '%',
backgroundColor:'rgba(21, 92, 84, 0.75)',
borderColor:'rgba(255, 0, 0, 1)',
borderWidth: 2,
}]
},
options: {
tooltips: {
// Disable the on-canvas tooltip
enabled: false,
mode: 'index',
intersect: false,
custom: function(tooltipModel) {
// Tooltip Element
var tooltipEl = document.getElementById('chartjs-tooltip');
// Create element on first render
if (!tooltipEl) {
tooltipEl = document.createElement('div');
tooltipEl.id = 'chartjs-tooltip';
tooltipEl.innerHTML = '<table class="bink-calc__tooltip"></table>';
document.body.appendChild(tooltipEl);
}
// Hide if no tooltip
if (tooltipModel.opacity === 0) {
tooltipEl.style.opacity = 0;
return;
}
// Set caret Position
tooltipEl.classList.remove('above', 'below', 'no-transform');
if (tooltipModel.yAlign) {
tooltipEl.classList.add(tooltipModel.yAlign);
} else {
tooltipEl.classList.add('no-transform');
}
function getBody(bodyItem) {
return bodyItem.lines;
}
// Set Text
if (tooltipModel.body) {
var titleLines = tooltipModel.title || [];
var bodyLines = tooltipModel.body.map(getBody);
var innerHtml = '<thead>';
titleLines.forEach(function(title) {
innerHtml += '<tr><th>' + title + '</th></tr>';
});
innerHtml += '</thead><tbody>';
bodyLines.forEach(function(body, i) {
var colors = tooltipModel.labelColors[i];
var style = 'background:' + colors.backgroundColor;
style += '; border-color:' + colors.borderColor;
style += '; border-width: 2px';
var span = '<span style="' + style + '"></span>';
innerHtml += '<tr><td>' + span + body + '</td></tr>';
});
innerHtml += '</tbody>';
var tableRoot = tooltipEl.querySelector('table');
tableRoot.innerHTML = innerHtml;
}
// `this` will be the overall tooltip
var position = this._chart.canvas.getBoundingClientRect();
// Display, position, and set styles for font
tooltipEl.style.opacity = 1;
tooltipEl.style.position = 'absolute';
tooltipEl.style.left = position.left + window.pageXOffset + tooltipModel.caretX + 'px';
tooltipEl.style.top = position.top + window.pageYOffset + tooltipModel.caretY + 'px';
tooltipEl.style.fontFamily = tooltipModel._bodyFontFamily;
tooltipEl.style.fontSize = tooltipModel.bodyFontSize + 'px';
tooltipEl.style.fontStyle = tooltipModel._bodyFontStyle;
tooltipEl.style.padding = tooltipModel.yPadding + 'px ' + tooltipModel.xPadding + 'px';
tooltipEl.style.pointerEvents = 'none';
tooltipEl.style.transition = '0.23s';
}
},
elements: {
line: {
tension: 0
},
point:{
radius: 0
}
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
fontSize: 16,
fontColor: 'rgba(0,0,0,1)',
},
gridLines: {
color: 'rgba(64,64,64,0.95)',
lineWidth: 2
},
scaleFontSize: 40
}],
xAxes: [{
ticks: {
fontSize: 16,
fontColor: 'rgba(0,0,0,1)',
},
gridLines: {
color: 'rgba(64,64,64,0.95)',
lineWidth: 2
},
type: 'time',
distribution: 'linear',
time: {
displayFormats: {
quarter: 'MMM YYYY'
}
}
}]
},
}
});
</script>
</section>
</div>
</body>
</html>