1π
For anyone interested I managed to figure this out, I ended up looking at the tooltip drawing functions within charts.js and using a modified version of this as a custom tooltip, thus drawing the tooltip after the annotations are added.
First add this to your opptions
config = {
options: {
tooltips: {
enabled: false,
custom: customTooltips
}
This then calls the custom tooltip function below.
var currentX = null;
var currentY = null;
var customTooltips = function (tooltip) {
var helpers = Chart.helpers;
var ctx = this._chart.ctx;
var vm = this._view;
if (vm == null || ctx == null || helpers == null || vm.opacity === 0) {
return;
}
var tooltipSize = this.getTooltipSize(vm);
var pt = {
x: vm.x,
y: vm.y
};
if (currentX == vm.x && currentY == vm.y) {
return;
}
currentX = vm.x;
currentY = vm.y;
// IE11/Edge does not like very small opacities, so snap to 0
var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
// Draw Background
var bgColor = helpers.color(vm.backgroundColor);
ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString();
helpers.drawRoundedRectangle(ctx, pt.x, pt.y, tooltipSize.width, tooltipSize.height, vm.cornerRadius);
ctx.fill();
// Draw Caret
this.drawCaret(pt, tooltipSize, opacity);
// Draw Title, Body, and Footer
pt.x += vm.xPadding;
pt.y += vm.yPadding;
// Titles
this.drawTitle(pt, vm, ctx, opacity);
// Body
this.drawBody(pt, vm, ctx, opacity);
// Footer
this.drawFooter(pt, vm, ctx, opacity);
};
6π
@user3284707 Actually what you have to do is draw the numbers on top of your bars before the tooltips, you are drawing them onComplete
, putting them on top of everything.
I draw those numbers using:
Chart.plugins.register({
beforeDraw: function(chartInstance) {
if (chartInstance.config.options.showDatapoints) {
var helpers = Chart.helpers;
var ctx = chartInstance.chart.ctx;
var fontColor = helpers.getValueOrDefault(chartInstance.config.options.showDatapoints.fontColor, chartInstance.config.options.defaultFontColor);
// render the value of the chart above the bar
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, 'normal', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
ctx.fillStyle = fontColor;
chartInstance.data.datasets.forEach(function (dataset) {
for (var i = 0; i < dataset.data.length; i++) {
var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
var scaleMax = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._yScale.maxHeight;
var yPos = (scaleMax - model.y) / scaleMax >= 0.93 ? model.y + 20 : model.y - 5;
var label = dataset.data[i] || '';
ctx.fillText(label.toLocaleString(), model.x, yPos);
}
});
}
}
});
Notice the beforeDraw
there.
Hope this helps 3 years later, I spent the last 30 minutes trying to fix this π€£
2π
If you are looking for the close solution of the code written in the question then here is the code:
let ctx2 = document.getElementById("barChart").getContext("2d");
let chart = new Chart(ctx2, {
type: 'bar',
data: {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
label: 'Months',
data: ['20','30','10','15','50','35','25'],
backgroundColor: 'rgba(26,179,148,0.5)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
},
options: {
legend: {
display: false
},
responsive: true,
maintainAspectRatio: true,
legendCallback: function(chart) {
var text = [];
for (var i=0; i<chart.data.datasets.length; i++) {
text.push(chart.data.labels[i]);
}
return text.join("");
},
tooltips: {
mode: 'index',
callbacks: {
// Use the footer callback to display the sum of the items showing in the tooltip
title: function(tooltipItem, data) {
let title_str = data['labels'][tooltipItem[0]['index']];
let lastIndex = title_str.lastIndexOf(" ");
return title_str.substring(0, lastIndex);
},
label: function(tooltipItem, data) {
return 'val: '+data['datasets'][0]['data'][tooltipItem['index']];
},
},
},
scales: {
xAxes: [{
stacked: false,
beginAtZero: true,
// scaleLabel: {
// labelString: 'Month'
// },
ticks: {
min: 0,
autoSkip: false,
maxRotation: 60,
callback: function(label, index, labels) {
return label;
}
}
}]
}
},
plugins:[{
afterDatasetsDraw: function(chart,options) {
// var chartInstance = chart,
let ctx = chart.ctx;
ctx.font = Chart.defaults.global.defaultFontStyle;
ctx.fillStyle = Chart.defaults.global.textColor;
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
chart.data.datasets.forEach(function (dataset, i) {
var meta = chart.controller.getDatasetMeta(i);
meta.data.forEach(function (bar, index) {
ctx.fillText(Math.round(dataset.data[index]), bar._model.x, bar._model.y - 5);
});
})
}
}]
});
document.getElementById('barChart').innerHTML = chart.generateLegend();
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<canvas id="barChart" height="140"></canvas>
</div>
Here, I have made use of plugin afterDatasetDraw
. https://www.chartjs.org/docs/latest/developers/plugins.html?h=afterdatasetsdraw
1π
If anyone would look for the solution for this issue, there is an easier way of achieving what OP needed.
Instead of drawing in onComplete
callback, draw it in afterDatasetsDraw
callback. Itβs being called just before the tooltip gets drawn.