Chartjs hovering over one Chart, shows tooltip across all charts - javascript

I am using ChartJS with angular (https://jtblin.github.io/angular-chart.js/)
I am able to get a vertical line in my chart when hovering using How to render a vertical line on hover in chartjs example.
I tried looking for examples of a curved line chart where the X-axis has a shared date range between all charts in DOM, but Y-axis has different values. Hovering over any chart will trigger hover over all available charts and display that vertical line like above with a tool-tip on all charts

tooltips: {
mode: 'x-axis'
},
If I understand what you want correctly, this should do it.

This jsfiddle may be able to help you:
https://jsfiddle.net/vikas12118/k4oveLsb/
var charts = [];
$(document).ready(function () {
Chart.defaults.LineWithLine = Chart.defaults.line;
Chart.controllers.LineWithLine = Chart.controllers.line.extend({
draw: function(ease) {
if (charts) {
for (var i = 0; i < charts.length; i++) {
charts[i].tooltip._active = [];
charts[i].tooltip.update(true);
charts[i].draw();
}
}
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.scales['y-axis-0'].top,
bottomY = this.chart.scales['y-axis-0'].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();
if(charts)
{
showTooltip(chart1.chart.tooltip._active[0]._index);
}
}
}
});
var ctx1 = document.getElementById('myChart1').getContext('2d');
var chart1 = new Chart(ctx1, {
type: 'LineWithLine',
data: {
labels: ['Segment 1', 'Segment 2', 'Segment 3','Segment 4','Segment 5','Segment 6','Segment 7','Segment 8','Segment 9','Segment 10','Segment 11','Segment 12'],
datasets: [{
lineTension: 0,
backgroundColor: "rgb(34,139,34)",
borderColor: "rgb(34,139,34)",
data: [14, 19, 20, 10, 6, 15, 8, 27, 25, 14, 36, 22],
fill: false,
pointRadius: 1.5,
pointHoverRadius: 1,
borderWidth :1.5
}],
},
options: {
maintainAspectRatio: false,
responsive: false,
/*legend: {
display: false
}, s: {
displayColors: false
},*/
hover: {
mode: 'index',
intersect: false,
},
title: {
display: true,
text: ''
},
legend: {
display: false
},
tooltips: {
mode: 'index',
//enabled: false,
intersect: false,
},
}
});
var ctx2 = document.getElementById('myChart2').getContext('2d');
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.scales['y-axis-0'].top,
bottomY = this.chart.scales['y-axis-0'].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(ctx2, {
type: 'LineWithLine',
data: {
labels: ['Segment 1', 'Segment 2', 'Segment 3','Segment 4','Segment 5','Segment 6','Segment 7','Segment 8','Segment 9','Segment 10','Segment 11','Segment 12'],
datasets: [{
lineTension: 0,
backgroundColor: "rgb(34,139,34)",
borderColor: "rgb(34,139,34)",
data: [14, 11, 10, 20, 20, 15, 25, 15, 13, 14, 16, 8],
fill: false,
pointRadius: 1.5,
pointHoverRadius: 1,
borderWidth :1.5
}],
},
options: {
maintainAspectRatio: false,
responsive: false,
title: {
display: true,
text: ''
},
legend: {
display: false
},
tooltips: {
mode: 'index',
//enabled: false,
intersect: false,
},
}
});
charts.push(chart)
});
function showTooltip(index) {
if (Array.isArray(charts) && charts.length) {
for (var i = 0; i < charts.length; i++) {
var segment = charts[i].getDatasetMeta(0).data[index];
charts[i].tooltip._active = [segment];
charts[i].tooltip.update(true);
charts[i].draw();
}
}
}
html content
<div>
<canvas style="width: 800px" height="300px" id="myChart1"></canvas></div>
<div>
<canvas style="width: 800px" height="300px" id="myChart2"></canvas></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>

Related

Draw a horizontal and vertical line on mouse hover in chart js

I am stuck with a problem on chart js while creating line chart. I want to create a chart with the specified data and also need to have horizontal and vertical line while I hover on intersection point. I am able to create vertical line on hover but can not find any solution where I can draw both the line. Here is my code to draw vertical line on hover.
window.lineOnHover = function(){
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 = 1;
ctx.setLineDash([3,3]);
ctx.strokeStyle = '#FF4949';
ctx.stroke();
ctx.restore();
}
}
});
}
//create chart
var backhaul_wan_mos_chart = new Chart(backhaul_wan_mos_chart, {
type: 'LineWithLine',
data: {
labels: ['Aug 1', 'Aug 2', 'Aug 3', 'Aug 4', 'Aug 5', 'Aug 6', 'Aug 7', 'Aug 8'],
datasets: [{
label: 'Series 1',
data: [15, 16, 17, 18, 16, 18, 17, 14, 19, 16, 15, 15, 17],
pointRadius: 0,
fill: false,
borderDash: [3, 3],
borderColor: '#0F1731',
// backgroundColor: '#FF9CE9',
// pointBackgroundColor: ['#FB7BDF'],
borderWidth: 1
}],
// lineAtIndex: 2,
},
options: {
tooltips: {
intersect: false
},
legend: {
display: false
},
scales: {
xAxes: [{
gridLines: {
offsetGridLines: true
},
ticks: {
fontColor: '#878B98',
fontStyle: "600",
fontSize: 10,
fontFamily: "Poppins"
}
}],
yAxes: [{
display: true,
stacked: true,
ticks: {
min: 0,
max: 50,
stepSize: 10,
fontColor: '#878B98',
fontStyle: "500",
fontSize: 10,
fontFamily: "Poppins"
}
}]
},
responsive: true,
}
});
my output of the code is as follow in WAN MoS Score graph --
So I want to have an horizontal line with the same vertical line together when I hover on the intersection (plotted) point..
Please help my guys..Thanks in advance.
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/3.8.0/chart.js"></script>
</body>
This is 2022, the current version of ChartJS is 4.0.1. So, I recommend to use this new implementation.
First, let's define a plugin. ChartJS's plugins has an id parameter, in this case less say corsair.
Then we define default variables for our plugin, like width, color and line dash. Additionally, our plugin will have three parameters: x, y, and draw. x and y are the values of the mousemove event and draw represents the inChartArea parameter, this parameter defines if the event occurred inside of the chart area or not.
Finally, we capture the afterDraw hook to draw a vertical and horizontal lines based on the x and y values if the event was triggered inside of the chart area.
ChartJS has various hooks to capture different parts of the chart render cycle.
const plugin = {
id: 'corsair',
defaults: {
width: 1,
color: '#FF4949',
dash: [3, 3],
},
afterInit: (chart, args, opts) => {
chart.corsair = {
x: 0,
y: 0,
}
},
afterEvent: (chart, args) => {
const {inChartArea} = args
const {type,x,y} = args.event
chart.corsair = {x, y, draw: inChartArea}
chart.draw()
},
beforeDatasetsDraw: (chart, args, opts) => {
const {ctx} = chart
const {top, bottom, left, right} = chart.chartArea
const {x, y, draw} = chart.corsair
if (!draw) return
ctx.save()
ctx.beginPath()
ctx.lineWidth = opts.width
ctx.strokeStyle = opts.color
ctx.setLineDash(opts.dash)
ctx.moveTo(x, bottom)
ctx.lineTo(x, top)
ctx.moveTo(left, y)
ctx.lineTo(right, y)
ctx.stroke()
ctx.restore()
}
}
const 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
}
]
}
const options = {
maintainAspectRatio: false,
hover: {
mode: 'index',
intersect: false,
},
plugins: {
corsair: {
color: 'black',
}
}
}
const config = {
type: 'line',
data,
options,
plugins: [plugin],
}
const $chart = document.getElementById('chart')
const chart = new Chart($chart, config)
<div class="wrapper" style="width: 98vw; height: 180px">
<canvas id="chart"></canvas>
</div>
<script src="https://unpkg.com/chart.js#4.0.1/dist/chart.umd.js"></script>
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)

Background color of the chart area in chartjs not working

I wanted to color the chart area of my line graph however it's not working:
options={{
maintainAspectRatio: false,
title: {
display: true,
fontSize: 20
},
chartArea: {
backgroundColor: "blue"
},
I have also recreated this in codesandbox:
https://codesandbox.io/s/practical-shadow-9he5y?file=/src/App.js:914-1118
There is no chartArea.backgroundColor option, you will need to use a custom plugin for this:
const plugin = {
id: 'background',
beforeDraw: (chart, args, opts) => {
if (!opts.color) {
return;
}
const {
ctx,
chartArea
} = chart;
ctx.fillStyle = opts.color;
ctx.fillRect(chartArea.left, chartArea.top, chartArea.width, chartArea.height)
}
}
Chart.register(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: {
background: {
color: 'cyan'
}
}
}
}
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/3.5.1/chart.js"></script>
</body>
I forked a sandbox from yours :
React Chartjs Canvas Background
You can register plugins in useEffect :
useEffect(() => {
Chart.register({
id: "custom_canvas_background_color",
beforeDraw: (chart) => {
console.log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
const ctx = chart.canvas.getContext("2d");
ctx.save();
ctx.globalCompositeOperation = "destination-over";
ctx.fillStyle = "lightGreen";
ctx.fillRect(0, 0, chart.width, chart.height);
ctx.restore();
}
});
}, []);
Also additional useful resources for you :
https://github.com/reactchartjs/react-chartjs-2
https://github.com/chartjs/Chart.js/tree/v3.5.1

How to plot a single value with line in line chart graph using charts.js?

I need to plot a single value in line chart. Currently i am using charts.JS library for line graph purpose.
The data will be varied some times i'll get the single data inside the data set at that time i need to plot the single value with line in the line chart.
I tried with the charts.js annotation plugin but it wasn't met my requirements. which is like it wis overlapping the plotted point in the graph area.
CODE WHICH I HAD TRIED
createLineChart() {
this.lineChart = new Chart(this.lineCanvas.nativeElement, {
type: "line",
data: {
labels:[],
datasets: [
{
fill: false,
backgroundColor: "#0168FF",
borderColor: "#0168FF",
pointBackgroundColor: "white", // wite point fill
pointBorderWidth: 1, // point border width
lineTension: 0,
pointBorderColor: "blue",
pointRadius: 4,
},
],
},
options: {
scales: {
yAxes: [
{
ticks: {
padding: 20,
beginAtZero: true,
min: 0,
stepSize: 100,
},
gridLines: {
drawBorder: false,
},
},
],
xAxes: [
{
// offset: true,
ticks: {
display: false,
//beginAtZero: true,
min: 0,
},
gridLines: {
zeroLineColor: "transparent",
drawBorder: false,
display: false,
},
//offset:true,
},
],
legend: {
display: false,
},
tooltips: {
enabled: false,
},
},
drawTime: "afterDraw", // (default)
} as ChartOptions,
// plugins: [ChartAnnotation]
},
});
}
To generate dynamic data and plot in the graph area.
generateRandomDataSet(size) {
let yaxisArr = [];
let xaxisArr = [];
let random_data:any = this.getRandomData(size)
let maxYTickVal = Math.max.apply(Math, random_data.map((val) => {return val.yaxis}));
let maxVal = Math.ceil((maxYTickVal+1) / 10) * 10
for(let data of random_data) {
yaxisArr.push(data.yaxis)
xaxisArr.push(data.xaxis)
}
console.log("X-Axis array values : "+xaxisArr)
console.log("Y-Axis array values : "+yaxisArr)
this.lineChart.data.datasets[0].data = yaxisArr
this.lineChart.config.data.labels = []
this.lineChart.config.data.labels = xaxisArr
this.lineChart.config.options.scales.yAxes[0].ticks.max =maxVal
this.lineChart.config.options.scales.yAxes[0].ticks.stepSize = maxVal/2
this.lineChart.update()
}
getRandomData(arraySize) {
let data = []
for(var i=1; i<=arraySize; i++) {
let number = Math.floor(Math.random() * 200) + 1
data.push({'xaxis':i,'yaxis':number})
}
return data
}
with the above code i am getting like
what i need to have
You can define an animation.onComplete function as follows to draw the line in case a single data value is present.
animation: {
onComplete: e => {
var ctx = chart.chart.ctx;
var data = chart.config.data.datasets[0].data;
if (data[0] == null) {
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
var y = yAxis.getPixelForValue(data[1]);
ctx.save();
ctx.globalCompositeOperation='destination-over';
ctx.strokeStyle = 'blue'
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(xAxis.left, y);
ctx.lineTo(xAxis.right, y);
ctx.stroke();
ctx.restore();
}
}
},
This function expects the data array to be of format [null, <value>, null] in case a single value is present, otherwise it will be hard to horizontally center the data point (see this answer). It's up to you to change the generateRandomDataSet() function in a way that it provides such data.
Please have a look at your changed code below.
const chart = new Chart('line-chart', {
type: "line",
data: {
labels: ['', 'A', ''],
datasets: [{
data: [null, 120, null],
fill: false,
backgroundColor: "#0168FF",
borderColor: "#0168FF",
pointBackgroundColor: "white",
pointBorderWidth: 1,
lineTension: 0,
pointBorderColor: "blue",
pointRadius: 4,
}],
},
options: {
legend: {
display: false
},
tooltips: {
enabled: false
},
animation: {
onComplete: e => {
var ctx = chart.chart.ctx;
var data = chart.config.data.datasets[0].data;
if (data[0] == null) {
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
var y = yAxis.getPixelForValue(data[1]);
ctx.save();
ctx.globalCompositeOperation='destination-over';
ctx.strokeStyle = 'blue'
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(xAxis.left, y);
ctx.lineTo(xAxis.right, y);
ctx.stroke();
ctx.restore();
}
}
},
scales: {
yAxes: [{
ticks: {
padding: 20,
min: 0,
stepSize: 100
},
gridLines: {
drawBorder: false
}
}],
xAxes: [{
ticks: {
display: false
},
gridLines: {
zeroLineColor: "transparent",
drawBorder: false,
display: false
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="line-chart" height="80"></canvas>

Label is not showing in ChartJS

I'm using ChartJS for my implementation of charts, but I notice that one of my graph's label is hidden. It is not showing its label above the bar. I've added a screenshot below for the comparison of two different bar graphs. The left graph shows the label even if it is on the very top but the other one is not showing. Please see my screenshot and code below.
function createChart(context, type, bgColor, bdColor, labels, actualData, options = {}){
new Chart(context, {
type: type,
data: {
labels: labels,
datasets: [{
label: "Actual",
backgroundColor: bgColor,
borderColor: bdColor,
data: actualData,
}]
},
options: options
});
}
function getOptions(displayLegend = true){
return {
events: false,
showTooltips: false,
legend: {
display: displayLegend
},
animation: {
duration: 0,
onComplete: function(){
var ctx = this.chart.ctx;
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseLine = 'bottom';
ctx.fillStyle = '#0b7707';
this.data.datasets.forEach(function(dataset){
console.log(dataset);
for(var i = 0; i < dataset.data.length; i++){
for(var key in dataset._meta){
var model = dataset._meta[key].data[i]._model;
ctx.fillText(dataset.data[i], model.x, model.y - 13);
}
}
});
}
}
};
}
I solved this problem by adding an empty title to the chart, so it will create space above the chart and show labels above the bar
options: {
title: {
display: true,
text: ' '
},
....
This looks like a clear case of the datalabels going out of the canvas since the bar takes height dynamically as per the data values. You can set the max y-tick setting to solve this. Here is the jsfiddle -> https://jsfiddle.net/Luaf2tm4/5979/
Hope it helps!
var canvas = document.getElementById('myChart');
var data = {
labels: ["January", "February", "March", "April", "May", "June"],
datasets: [{
label: "My First dataset",
backgroundColor: "rgba(255,99,132,0.2)",
borderColor: "rgba(255,99,132,1)",
borderWidth: 2,
hoverBackgroundColor: "rgba(255,99,132,0.4)",
hoverBorderColor: "rgba(255,99,132,1)",
data: [500, 2000, 800, 600, 950, 890],
}]
};
function getOptions(displayLegend = false) {
return {
events: false,
showTooltips: false,
legend: {
display: displayLegend
},
scales: {
yAxes: [{
display: true,
stacked: true,
ticks: {
stepSize: 200,
min: 0, // minimum value
max: 2200 // maximum value, you can either hard code if you know your datainput, else computer the value through some logic i.e taking the max value from the dataset and adding some extra value to it.
}
}]
},
animation: {
duration: 0,
onComplete: function() {
var ctx = this.chart.ctx;
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily);
ctx.textAlign = 'center';
ctx.textBaseLine = 'bottom';
ctx.fillStyle = '#0b7707';
this.data.datasets.forEach(function(dataset) {
console.log(dataset);
for (var i = 0; i < dataset.data.length; i++) {
for (var key in dataset._meta) {
var model = dataset._meta[key].data[i]._model;
ctx.fillText(dataset.data[i], model.x, model.y - 10);
}
}
});
}
}
};
}
var myBarChart = Chart.Bar(canvas, {
data: data,
options: getOptions()
});

Chart.JS Error: this.scale is undefined

I'm trying since to days and really new to Chart.js. Everything seems to be clear but now i would like to put a label on top of every single bar.
Trying this i get an error: this.scale is undefined. I got the animation.onComplete Snippet out of the net but it seems i make a mistake. The Chart works fine .. i just don't get the labels on top of the bars. Maybe someone can please help me with this ?!
I also have a line chart with the same problem.
var ctx = document.getElementById("chartA").getContext("2d");
Chart.defaults.global.animation.duration = 2400;
Chart.defaults.global.animation.easing = "easeInOutQuad";
Chart.defaults.global.elements.point.radius = 4;
Chart.defaults.global.elements.point.hoverRadius = 5;
Chart.defaults.global.elements.point.hitRadius = 1;
var chart = new Chart(ctx, {
type: "bar",
data: {
labels: ["A","B","C"],
datasets: [{
label: "Test",
backgroundColor: "rgba(255, 99, 132, 0.2)",
borderColor: "#CF2748",
borderWidth: 1,
data: [10,20,30]
}]
},
options: {
tooltips: { mode: 'nearest', intersect: false },
layout: { padding: { left: 20, right: 0, top: 0, bottom: 0 } },
legend: { display: true, position: 'top' },
scales: {
yAxes: [{
ticks: { maxTicksLimit: 9, stepSize: 300, callback: function(value, index, values) { return value+" €"; } }
}]
},
animation: {
onComplete: function () {
var ctx = this.chart.ctx; // this part doesn't work
ctx.font = this.scale.font;
ctx.fillStyle = this.scale.textColor;
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
this.datasets.forEach(function (dataset) {
dataset.bars.forEach(function (bar) {
ctx.fillText(bar.value, bar.x, bar.y - 5);
});
});
}
}
}
});
Thank you so much
Oliver
Thanks #Jeff I was testing around and get closer.
chart.data.datasets.forEach(function (dataset) {
dataset.data.forEach(function (value) {
ctx.fillText(value, x, y);
});
});
Now i have in "value" the right value. But i need to refer X and Y. Where do i get them? If i change X and Y with static value it works but all values were logically printed on the same space.
There are several issues with your code.
You could rather use the following chart plugin to accomplish the same :
Chart.plugins.register({
afterDatasetsDraw: function(chart) {
var ctx = chart.ctx;
chart.data.datasets.forEach(function(dataset, datasetIndex) {
var datasetMeta = chart.getDatasetMeta(datasetIndex);
datasetMeta.data.forEach(function(meta) {
var posX = meta._model.x;
var posY = meta._model.y;
var value = chart.data.datasets[meta._datasetIndex].data[meta._index];
// draw values
ctx.save();
ctx.textBaseline = 'bottom';
ctx.textAlign = 'center';
ctx.font = '16px Arial';
ctx.fillStyle = 'black';
ctx.fillText(value, posX, posY);
ctx.restore();
});
});
}
});
add this plugin at the beginning of your script.
ᴡᴏʀᴋɪɴɢ ᴇxᴀᴍᴘʟᴇ ⧩
Chart.plugins.register({
afterDatasetsDraw: function(chart) {
var ctx = chart.ctx;
chart.data.datasets.forEach(function(dataset, datasetIndex) {
var datasetMeta = chart.getDatasetMeta(datasetIndex);
datasetMeta.data.forEach(function(meta) {
var posX = meta._model.x;
var posY = meta._model.y;
var value = chart.data.datasets[meta._datasetIndex].data[meta._index];
// draw values
ctx.save();
ctx.textBaseline = 'bottom';
ctx.textAlign = 'center';
ctx.font = '16px Arial';
ctx.fillStyle = 'black';
ctx.fillText(value, posX, posY);
ctx.restore();
});
});
}
});
var ctx = document.getElementById("chartA").getContext("2d");
Chart.defaults.global.animation.duration = 2400;
Chart.defaults.global.animation.easing = "easeInOutQuad";
Chart.defaults.global.elements.point.radius = 4;
Chart.defaults.global.elements.point.hoverRadius = 5;
Chart.defaults.global.elements.point.hitRadius = 1;
var chart = new Chart(ctx, {
type: "bar",
data: {
labels: ["A", "B", "C"],
datasets: [{
label: "Test",
backgroundColor: "rgba(255, 99, 132, 0.2)",
borderColor: "#CF2748",
borderWidth: 1,
data: [10, 20, 30]
}]
},
options: {
tooltips: {
mode: 'nearest',
intersect: false
},
layout: {
padding: {
left: 20,
right: 0,
top: 0,
bottom: 0
}
},
legend: {
display: true,
position: 'top'
},
scales: {
yAxes: [{
ticks: {
maxTicksLimit: 9,
stepSize: 300,
callback: function(value, index, values) {
return value + " €";
}
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="chartA"></canvas>

Categories