Chart.js datalabels bring label to foreground on hover - chart.js

I'm using the Datalabels plugin to provide labels to points on a Chart.js scatter graph.
My Chart.js / Datalabels configuration looks like this:
(Full example: https://codepen.io/LondonAppDev/pen/oNyReVr)
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'scatter',
data: {
datasets: [{
label: '# of Votes',
data: [
{x: 10, y: 10},
{x: 100, y: 100},
{x: 50, y: 50},
{x: 51, y: 51},
]
}]
},
plugins: [ChartDataLabels],
options: {
elements: {
point: {
radius: 5,
hoverRadius: 8
}
},
plugins: {
tooltip: {
enabled: false
},
datalabels: {
labels: {
value: {
align: 'top',
color: '#000',
borderColor: '#000',
borderWidth: 1,
offset: 20,
padding: 10,
backgroundColor: '#fff',
}
}
}
}
}
});
The default behaviour is that labels will overlap. I want to make the labels come to the forefront when they are hovered over. So if I hover over a label or point, it comes to the foreground.
I attempted to add this to options.plugins.datalabels.labels.value:
listeners: {
enter: function(context) {
context.dataIndex = 999;
context.datasetIndex = 999;
return true;
},
leave: function(context) {
context.dataIndex = 0;
context.datasetIndex = 0;
return true;
}
}
However it doesn't have the desired effect.
Anyone know how I can make the label come to the foreground on hover?

Related

Fill gap between stacked bars in chart.js

I'm currently using chart.js with Vue 2 and I'm facing a problem when using border-radius with stacked bars.
I want to have a border-top radius but when doing that I get a "gap" between the border that was created and the bar above.
Is there any option that can fill that empty space?
Current behaviour
Expected behaviour
These are the current options I'm using for the graph.
options: {
responsive: true,
lineTension: 1,
plugins: {
legend: {
position: 'bottom',
// prevent filtering using labels
'onClick' : function (evt, item, legend) {
return;
},
labels: {
usePointStyle: 'circle',
},
}
},
elements: {
bar: {
borderRadius: {
topLeft: 4.6,
topRight: 4.6,
},
}
},
scales: {
y: {
stacked: true,
ticks: {
maxTicksLimit: 6,
callback: function(value, index, ticks) {
return value + ' min';
}
},
},
x: {
stacked: true,
grid: {
color: "rgba(0, 0, 0, 0)",
}
}
}
}
What you need it is not configurable by any options.
Nevertheless you can use a plugin to change the base of the bar to draw, taking care of the border radius.
EDIT: I have found a specific option in bar element, base, which should address the use case.
See snippet:
const ctx = document.getElementById("myChart");
// not used but leave it
const plugin = {
id: 'fillGaps',
beforeDatasetDraw(chart, args) {
if (args.index > 0) {
args.meta.data.forEach(function(item){
const pre = item.base;
item.base += 12;
});
}
},
};
//
const myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['January', 'Fabruary', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
label: 'user 1 online',
data: [50, 35, 45, 47, 10, 3, 27],
backgroundColor: 'rgba(40, 139, 170, 1)',
borderWidth: 0,
borderSkipped: 'start',
base: 0
},
{
label: 'user 2 online',
data: [50, 35, 45, 47, 10, 3, 27],
backgroundColor: 'orange',
borderWidth: 0,
borderSkipped: 'start',
base: 0
}]
},
options: {
elements: {
bar: {
borderRadius: {
topLeft: 12,
topRight: 12,
bottomLeft: 12,
bottomRight: 12
},
}
},
scales: {
y: {
stacked: true,
},
x: {
stacked: true,
}
}
}
});
.myChartDiv {
max-width: 600px;
max-height: 400px;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.9.1/dist/chart.min.js"></script>
<html>
<body>
<div class="myChartDiv">
<canvas id="myChart" width="600" height="400"/>
</div>
</body>
</html>

Chartjs how to show scale label horizontally

Due to very poor documentation (https://www.chartjs.org/docs/latest/) I decided to ask Chart.js community this question.
How can I change the angle of the scale label?
This is my actual view
I would like to make those labels horizontal (see red labels how it should be).
The config is:
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: [this.label, this.valueUnit],
fontSize: 14,
},
afterFit: function(scaleInstance) {
scaleInstance.width = 120;
}
}]
You can define a second y-axis that is responsible for drawing the scale label horizontally.
The single yAxis.ticks label can be left aligned by defining mirror: true together with some padding.
ticks: {
mirror: true,
padding: 60,
...
To make the tick label visible on the chart area, the same padding needs to be defined left of the chart layout.
layout: {
padding: {
left: 60
}
},
Please take a look on the runnable code below and see hot it works.
new Chart(document.getElementById('myChart'), {
type: 'line',
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
data: [10, 12, 8, 6],
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgb(255, 99, 132)',
borderWidth: 1,
fill: false
}]
},
options: {
layout: {
padding: {
left: 60
}
},
legend: {
display: false
},
scales: {
yAxes: [{
},
{
ticks: {
stepSize: 0.5,
mirror: true,
padding: 60,
fontColor: 'red',
callback: v => v == 0.5 ? ['Horizontal', 'Label'] : undefined
},
gridLines: {
display: false
}
}
]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="50"></canvas>
The Plugin Core API offers a range of hooks that may be used for performing custom code. You can use the afterDraw hook to draw the scale label yourself directly on the canvas using CanvasRenderingContext2D.fillText().
afterDraw: chart => {
var ctx = chart.chart.ctx;
ctx.save();
let yAxis = chart.scales['y-axis-0'];
let y = yAxis.bottom / 2;
ctx.textAlign = 'left';
ctx.font = "14px Arial";
ctx.fillStyle = "gray";
ctx.fillText('Horizontal', 0, y - 8);
ctx.fillText('Label', 0, y + 8);
ctx.restore();
}
You'll also have to define some extra padding at the left of the chart to make sure, the scale label does not overlap the chart area.
options: {
layout: {
padding: {
left: 70
}
},
Please take a look at the following runnable code and see how it works.
new Chart(document.getElementById('myChart'), {
type: 'line',
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
ctx.save();
let yAxis = chart.scales['y-axis-0'];
let y = yAxis.bottom / 2;
ctx.textAlign = 'left';
ctx.font = "14px Arial";
ctx.fillStyle = "gray";
ctx.fillText('Horizontal', 0, y - 8);
ctx.fillText('Label', 0, y + 8);
ctx.restore();
}
}],
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
data: [10, 12, 8, 6],
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgb(255, 99, 132)',
borderWidth: 1,
fill: false
}]
},
options: {
layout: {
padding: {
left: 70
}
},
legend: {
display: false
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="50"></canvas>
There is no build in way to do this. If you want to achieve this behaviour you will have to draw it on the canvas yourself with an custom plugin

Chartjs Datasets overlapping and z-index

I have the below Chart implemented using chart.js version 3.x.
https://jsfiddle.net/Lxya0u98/12/
I have multiple datasets in my charts to achieve the behavior I want.
I am facing an issue with the datasets overlapping. In the chart, I have end of the blue color line overlapping with the green color dot dataset. Is there a way to avoid this issue?
I have the below two datasets:
// Data set for the Big Dot
{
showLine: false,
borderWidth: 0,
lineTension: 0,
borderColor: colorVal,
backgroundColor: colorVal,
pointBackgroundColor: colorVal,
pointBorderColor: colorVal,
pointBorderWidth: 2,
pointRadius: 15,
};
// Data set for the Connecting Lines
{
showLine: true,
lineTension: 0,
borderWidth: 5,
borderColor: colorVal,
pointRadius: 0,
pointBorderWidth: 0,
spanGaps: true,
};
Is there a Z-Index for the Datasets so that they appear on top of the previous one in the stack?
The option dataset.order has similar effect as the z-index.
Datasets with higher order are drawn first
Datasets with no or lower order are drawn last, hence appear on top
Therefore, adding order: 1 to your line datasets should solve the problem.
var newDataLine = {
...
order: 1
};
Instead of defining multiple datasets, you could proceed as follows:
First convert your line chart into a scatter chart.
Then draw the lines directly on the canvas using the Plugin Core API. The API offers a range of hooks that may be used for performing custom code. You can use the beforeDraw hook to draw connection lines of different colors between data points and to the open end of the chart.
Note that you have to define xAxes.ticks.max in order to obtain the open end line at the right of the chart.
Please take a look at below runnable code snippet and see how it works.
new Chart('line-chart', {
type: "scatter",
plugins: [{
beforeDraw: chart => {
var ctx = chart.chart.ctx;
ctx.save();
var xAxis = chart.scales['x-axis-1'];
var yAxis = chart.scales['y-axis-1'];
var dataset = chart.data.datasets[0];
var y = yAxis.getPixelForValue(0);
dataset.data.forEach((value, index) => {
var xFrom = xAxis.getPixelForValue(value.x);
var xTo;
if (index + 1 < dataset.data.size) {
xTo = xAxis.getPixelForValue(dataset.data[index + 1].x);
} else {
xTo = xAxis.right;
}
ctx.strokeStyle = dataset.backgroundColor[index];
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(xFrom, y);
ctx.lineTo(xTo, y);
ctx.stroke();
});
ctx.restore();
}
}],
data: {
datasets: [{
data: [
{ x: 0, y: 0 },
{ x: 1, y: 0 },
{ x: 2, y: 0 }
],
backgroundColor: ['red', 'blue', 'green'],
borderColor: ['red', 'blue', 'green'],
pointRadius: 8,
pointHoverRadius: 8,
}],
},
options: {
layout: {
padding: {
left: 10,
right: 10
}
},
legend: {
display: false
},
tooltips: {
enabled: false
},
scales: {
yAxes: [{
ticks: {
display: false
},
gridLines: {
display: false,
}
}],
xAxes: [{
ticks: {
display: false,
max: 3
},
gridLines: {
display: false
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="line-chart" height="30"></canvas>

Adding line over stacked line chart with ChartJS

I'm trying to recreate this below chart with a stacked option on my background lines
But my attempts were unsuccessful with this image below as result
$(function() {
var areaChartCanvas = $('#areaChart').get(0).getContext('2d')
var areaChartData = {
labels: ['', '', ''],
datasets: [{
backgroundColor: 'transparent',
borderColor: 'black',
pointRadius: false,
data: [32, 12, 28],
type: 'line'
}, {
backgroundColor: 'red',
pointRadius: false,
data: [20, 20, 20]
}, {
backgroundColor: 'orange',
pointRadius: false,
data: [40, 40, 40]
}, {
backgroundColor: 'cyan',
pointRadius: false,
data: [60, 60, 60]
}]
}
var areaChartOptions = {
maintainAspectRatio: false,
responsive: true,
legend: {
display: false
},
scales: {
xAxes: [{
gridLines: {
display: true,
}
}],
yAxes: [{
gridLines: {
display: true,
},
stacked: true
}]
}
}
var areaChart = new Chart(areaChartCanvas, {
type: 'line',
data: areaChartData,
options: areaChartOptions
})
})
<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>
<canvas id="areaChart" style="height:250px"></canvas>
ideally, I want to be able to create 'AREAS' with different colors that will be stacked according to the interval I pass to it.
e.g:
cyan - 20
orange - 20
red - 20
but currently, I'm doing
cyan - 60
orange - 40
red - 20
If I understand you correctly, I thought about different approach and extends the chart with Plugin[1] (Inspired by https://stackoverflow.com/a/49303362/863110)
Chart.pluginService.register({
beforeDraw: function (chart, easing) {
if (chart.config.options.fillColor) {
var ctx = chart.chart.ctx;
var chartArea = chart.chartArea;
ctx.save();
let delta = 0;
const chartHeight = chartArea.bottom - chartArea.top;
const bottomBarHeight = chart.height - chartHeight - chartArea.top;
chart.config.options.fillColor.map(color => {
const colorHeight = chartHeight * (color[0] / 100);
const colorBottom = chartArea.bottom + colorHeight;
ctx.fillStyle = color[1];
const x = chartArea.left,
y = chart.height - bottomBarHeight - colorHeight - delta,
width = chartArea.right - chartArea.left,
height = colorHeight;
delta += height;
ctx.fillRect(x, y, width, height);
ctx.restore();
})
}
}
});
var chartData = {
labels: ['a', 'b', 'c', 'd'],
datasets: [{
label: 'value',
borderColor: 'blue',
data: [30, 50, 25, 10]
}]
};
var ctx = document.getElementById("myChart").getContext("2d");
var myBar = new Chart(ctx, {
type: 'line',
data: chartData,
options: {
scales: {
yAxes: [{ ticks: { max: 60 } }]
},
legend: { display: false },
fillColor: [
[20, 'red'],
[20, 'blue'],
[20, 'green'],
[20, 'pink'],
[20, 'yellow'],
]
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<canvas id="myChart" height="300" width="500"></canvas>
https://jsbin.com/lisuvuq/2/edit?js,output
[1] - With Plugin you can custom the chart's behaviour. When you use Plugin you get an API so you can "listen" to life cycle events of the chart (beforeDraw for example) and call your own code. The API calls your function and gives you data about the chart so you can use it in your code.In this example, we're using the API to (1) Run a code before the chart been drawing (2) Using the data to calculate the areas of the different colors. (3) Draw additional shapes (ctx.fillRect) based on the calculation.

ChartJS with Dynamic Colors

I saw your post regarding,
Instead of just "Stock" at the left, is there anyway to put a separate label for each bar, eg 18, 82, 22, 80 going down the y-axis?
Below is the link which I'm referring ...
ChartJS bar chart with legend which corresponds to each bar
You are looking for something like the following:
var chart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: [18, 82, 22, 80],
datasets: [{
data: [1, 2, 3, 4],
backgroundColor: ['#ff6384', '#36a2eb', '#ffce56', '#4bc0c0', '#9966ff'],
borderColor: ['#ff6384', '#36a2eb', '#ffce56', '#4bc0c0', '#9966ff']
}]
},
options: {
scales: {
xAxes: [{
ticks: {
beginAtZero: true
}
}]
},
legend: {
labels: {
generateLabels: function(chart) {
var labels = chart.data.labels;
var dataset = chart.data.datasets[0];
var legend = labels.map(function(label, index) {
return {
datasetIndex: 0,
text: label,
fillStyle: dataset.backgroundColor[index],
strokeStyle: dataset.borderColor[index],
lineWidth: 1
}
});
return legend;
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="ctx"></canvas>