I have a chart as follows, I only need one pair of legends (data 1 and data 2) to show on the chart.
In ChartJS v2, I can use generateLabels like this but it doesn't seem to work the same way in v3 that I'm using.
Is there an easy way to achieve this in v3 using generateLabels or do I have to change the DOM structure?
const trend_options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
datalabels: {
formatter: function(value, ctx) {
const otherDatasetIndex = ctx.datasetIndex % 2 === 0 ? ctx.datasetIndex + 1 : ctx.datasetIndex - 1;
const total = ctx.chart.data.datasets[otherDatasetIndex].data[ctx.dataIndex] + value;
return `${(value / total * 100).toFixed()}%`;
},
font: {
weight: "bold"
},
color: "#fff",
display: function(context) {
let index = context.dataIndex;
let value = context.dataset.data[index];
return value > 0; // display labels with a value greater than 0
}
},
},
scales: {
x: {
stacked: true
},
y: {
stacked: true,
suggestedMax: 15,
ticks: {
beginAtZero: true,
stepSize: 5,
}
}
},
};
const app_data_trend = [{
label: 'data 1',
data: [3, 4, 5, 6, 4, 4, 4],
backgroundColor: '#007bff',
stack: 'Stack 0',
},
{
label: 'data 2',
data: [3, 4, 5, 4, 3, 2, 3],
backgroundColor: '#6DB526',
stack: 'Stack 0',
},
{
label: 'data 1',
data: [4, 4, 4, 3, 2, 5, 4],
backgroundColor: '#007bff',
stack: 'Stack 1',
},
{
label: 'data 2',
data: [4, 2, 5, 2, 3, 3, 4],
backgroundColor: '#6DB526',
stack: 'Stack 1',
},
{
label: 'data 1',
data: [2, 4, 5, 2, 4, 5, 3],
backgroundColor: '#007bff',
stack: 'Stack 2',
},
{
label: 'data 2',
data: [1, 1, 2, 4, 4, 3, 5],
backgroundColor: '#6DB526',
stack: 'Stack 2',
},
]
const ctx8 = document.getElementById("tsa-applications-trending");
const chart8 = new Chart(ctx8, {
plugins: [ChartDataLabels],
type: 'bar',
data: {
labels: ['person1', 'person2', 'person3', 'person4'],
datasets: app_data_trend
},
options: trend_options,
});
.chart-container{
position: relative; height:80vh; width:80vw
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.7.1/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.0.0/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels#2.0.0"></script>
<div class="chart-container">
<canvas id="tsa-applications-trending"></canvas>
</div>
options.plugins.legend.labels.generateLabels can still be used in Chart.js v3. For your case, this could look as follows:
generateLabels: chart => chart.data.labels.slice(0, 2).map((l, i) => ({
datasetIndex: i,
text: l,
fillStyle: chart.data.datasets[i].backgroundColor,
strokeStyle: chart.data.datasets[i].backgroundColor,
hidden: chart.getDatasetMeta(i).hidden
}))
const trend_options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
datalabels: {
formatter: function(value, ctx) {
const otherDatasetIndex = ctx.datasetIndex % 2 === 0 ? ctx.datasetIndex + 1 : ctx.datasetIndex - 1;
const total = ctx.chart.data.datasets[otherDatasetIndex].data[ctx.dataIndex] + value;
return `${(value / total * 100).toFixed()}%`;
},
font: {
weight: "bold"
},
color: "#fff",
display: function(context) {
let index = context.dataIndex;
let value = context.dataset.data[index];
return value > 0; // display labels with a value greater than 0
}
},
legend: {
labels: {
generateLabels: chart => chart.data.labels.slice(0, 2).map((l, i) => ({
datasetIndex: i,
text: l,
fillStyle: chart.data.datasets[i].backgroundColor,
strokeStyle: chart.data.datasets[i].backgroundColor,
hidden: chart.getDatasetMeta(i).hidden
}))
}
},
},
scales: {
x: {
stacked: true
},
y: {
stacked: true,
suggestedMax: 15,
ticks: {
beginAtZero: true,
stepSize: 5,
}
}
},
};
const app_data_trend = [{
label: 'data 1',
data: [3, 4, 5, 6, 4, 4, 4],
backgroundColor: '#007bff',
stack: 'Stack 0',
},
{
label: 'data 2',
data: [3, 4, 5, 4, 3, 2, 3],
backgroundColor: '#6DB526',
stack: 'Stack 0',
},
{
label: 'data 1',
data: [4, 4, 4, 3, 2, 5, 4],
backgroundColor: '#007bff',
stack: 'Stack 1',
},
{
label: 'data 2',
data: [4, 2, 5, 2, 3, 3, 4],
backgroundColor: '#6DB526',
stack: 'Stack 1',
},
{
label: 'data 1',
data: [2, 4, 5, 2, 4, 5, 3],
backgroundColor: '#007bff',
stack: 'Stack 2',
},
{
label: 'data 2',
data: [1, 1, 2, 4, 4, 3, 5],
backgroundColor: '#6DB526',
stack: 'Stack 2',
},
]
const ctx8 = document.getElementById("tsa-applications-trending");
const chart8 = new Chart(ctx8, {
plugins: [ChartDataLabels],
type: 'bar',
data: {
labels: ['person1', 'person2', 'person3', 'person4'],
datasets: app_data_trend
},
options: trend_options,
});
.chart-container {
position: relative;
height: 80vh;
width: 80vw
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.7.1/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels#2.0.0"></script>
<div class="chart-container">
<canvas id="tsa-applications-trending"></canvas>
</div>
The problem with the approach is, that the displayed legend elements are still tied to individual datasets only. If you want to show/hide other datasets as well, you also need to define a legend.onClick function, similar to the one from this answer.
Related
I cannot get the annotation line to work on a stacked Bar Chart.
Example JS Fiddle: https://jsfiddle.net/cledwyn/rd5q6Lsz/10/
I have the standard Annotation per https://www.chartjs.org/chartjs-plugin-annotation/latest/samples/charts/bar.html
const annotation2 = {
type: 'line',
scaleID: 'x',
borderWidth: 3,
borderColor: 'black',
value: 8,
label: {
rotation: 'auto',
position: 'start',
backgroundColor: 'black',
content: 'Line at x=Label 5',
enabled: true
}
};
but when I stack the bar charts
scales: {
xAxes: { stacked: true },
yAxes: { stacked: true }
},
then the annotation line just goes from one corner of the chart to the other.
When running your JSFiddle, you can see the following warning on the console.
"No scale found with id 'x' for annotation 'annotation2'"
Therefore, changing scales.xAxes to scales.x should solve part of your problem.
scales: {
x: { stacked: true },
y: { stacked: true }
}
You'll further have to add missing labels to data.labels and adjust annotation2.value.
Please take a look at your amended code below and see how it works.
const labels = ['Label 1', 'Label 2', 'Label 3', 'Label 4', 'Label 5'];
const data = {
labels: labels,
datasets: [{
label: 'one',
data: [14, 21, 4, 5, 7],
backgroundColor: 'rgb(255, 99, 132)'
}, {
label: 'two',
data: [1, 3, 2, 4, 5],
backgroundColor: 'rgb(255, 199, 132)'
}, {
label: 'three',
data: [7, 1, 9, 6, 7],
backgroundColor: 'rgb(255, 99, 32)'
}]
};
const annotation2 = {
type: 'line',
scaleID: 'x',
borderWidth: 3,
borderColor: 'black',
value: 4,
label: {
rotation: 'auto',
position: 'start',
backgroundColor: 'black',
content: 'Line at x=Label 5',
enabled: true
}
};
const config = {
type: 'bar',
data: data,
options: {
plugins: {
annotation: {
annotations: {
annotation2,
}
}
},
scales: {
x: {
stacked: true
},
y: {
stacked: true
}
},
},
};
const chart = new Chart('chart', config);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.8.0/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-annotation/1.4.0/chartjs-plugin-annotation.min.js"></script>
<canvas id="chart" height="110"></canvas>
I am using chartjs-plugin-datalabels to display data labels in stacked bar chart using Chart.js
const stackedConfig = {
type: 'bar',
data: {
labels: ['Manager 1', 'Manager 2', 'Manager 3', 'Manager 4', 'Manager 5'],
datasets: [{
label: 'DATA1',
data: [2, 8, 3, 4, 5],
backgroundColor: '#22242C'
}, {
label: 'DATA2',
data: [1, 2, 3, 4, 5],
backgroundColor: '#FFA755'
}, {
label: 'DATA3',
data: [3, 1, 3, 4, 5],
backgroundColor: '#B48DD3'
}, {
label: 'DATA4',
data: [5, 2, 3, 4, 5],
backgroundColor: '#6160DC'
}, {
label: 'DATA5',
data: [2, 1, 3, 4, 5],
backgroundColor: '#54C5EB'
}, {
label: 'DATA5',
data: [1, 2, 3, 4, 5],
backgroundColor: '#FF4A55'
}],
barPercentage: 0.5
},
plugins: [ChartDataLabels],
options: {
barPercentage: 0.5,
plugins: {
legend: {
position: 'right',
display: false,
},
datalabels: {
anchor: 'center',
align: 'center',
color: 'white',
font: {
weight: 'bold'
},
formatter: Math.round
},
},
responsive: true,
scales: {
x: {
stacked: true,
},
y: {
stacked: true
}
}
}
};
var stackedCtx = document.getElementById("stacked-1-canvas").getContext('2d');
new Chart(stackedCtx, stackedConfig);
Even after putting anchor: 'center' in datalabels option, the labels are anchor at start. Even if I leave out the option, the default anchor should be center, but even then its anchored to the start position.
Please help me sort this out.
I need to hide the inside datalabels if there is only one dataset which is greater than zero. What I mean by dataset is
series: [{
name: 'John',
data: [5, 3, 0, 7, 2]
}, {
name: 'Jane',
data: [2, 2, 0, 2, 0]
}, {
name: 'Joe',
data: [3, 4, 3, 2, 0]
}]
If series.data[i] all are zero except one then hide the inside data labels. In the above case the 3rd and 5th dataset has 0,0,3 and 2,0,0 values only 1 non zero value so hide the inside datalabel.
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Stacked column chart'
},
xAxis: {
categories: ['Apples', 'Oranges', 'Pears', 'Grapes', 'Bananas']
},
yAxis: {
min: 0,
title: {
text: 'Total fruit consumption'
},
stackLabels: {
enabled: true,
style: {
fontWeight: 'bold',
color: ( // theme
Highcharts.defaultOptions.title.style &&
Highcharts.defaultOptions.title.style.color
) || 'gray'
}
}
},
legend: {
align: 'right',
x: -30,
verticalAlign: 'top',
y: 25,
floating: true,
backgroundColor:
Highcharts.defaultOptions.legend.backgroundColor || 'white',
borderColor: '#CCC',
borderWidth: 1,
shadow: false
},
tooltip: {
headerFormat: '<b>{point.x}</b><br/>',
pointFormat: '{series.name}: {point.y}<br/>Total: {point.stackTotal}'
},
plotOptions: {
column: {
stacking: 'normal',
dataLabels: {
enabled: true,
formatter:function() {
if(this.y != 0) {
return this.y;
}
}
}
}
},
series: [{
name: 'John',
data: [5, 3, 0, 7, 2]
}, {
name: 'Jane',
data: [2, 2, 0, 2, 0]
}, {
name: 'Joe',
data: [3, 4, 3, 2, 0]
}]
});
Here I have highlighted which datalabel should need to be remove from inside..
In the formatter function you can check if at least two of the series have y value greater than 0.
plotOptions: {
column: {
stacking: 'normal',
dataLabels: {
enabled: true,
formatter: function() {
var series = this.series.chart.series,
xPos = this.point.x,
filteredSeries;
if (this.y != 0) {
filteredSeries = series.filter((s) => (s.yData[xPos]));
return filteredSeries.length > 1 ? this.y : '';
}
}
}
}
}
Live demo: http://jsfiddle.net/BlackLabel/6m4e8x0y/4971/
API Reference: https://api.highcharts.com/highcharts/series.column.dataLabels.formatter
I need to show tooltips for only the last two series when hovering the mouse over the graph. In this example the idea will be to show only the tool tips for the values 1699 and 1750. The rest of the values can't show any tooltips when hovering the mouse. How can I achive that? I haven't found any way to do so. Is it possible? Thanks in advance.
(function () {
var categories= ['1350', '1400', '1450', '1500', '1550', '1699', '1750'];
Highcharts.chart('grafica1', {
chart: {
type: 'area',
},
title: {
text: ' '
},
xAxis: {
labels: {
enabled: true,
formatter: function () {
return categories[this.value];
}
},
tickmarkPlacement: 'on',
title: {
enabled: false
}
},
yAxis: {
title: {
text: 'Percent'
}
},
tooltip: {
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.percentage:.1f}%</b> ({point.y:,.0f} millions)<br/>',
split: true
},
plotOptions: {
area: {
stacking: 'percent',
lineColor: '#ffffff',
lineWidth: 1,
marker: {
lineWidth: 1,
lineColor: '#ffffff',
}
}
},
series: [{
name: 'Africa',
data: [502, 635, 809, 947, 1402, 3634, 5268]
}, {
name: 'L. America',
data: [106, 107, 111, 133, 221, 767, 1766]
}, {
name: 'Oceania',
data: [163, 203, 276, 408, 547, 729, 628]
}, {
name: 'S-E. Asia',
data: [18, 31, 54, 156, 339, 818, 1201]
}, {
name: 'Japan',
data: [2, 2, 2, 6, 13, 30, 46]
}, {
name: 'China',
data: [2, 2, 2, 6, 13, 30, 46]
}, {
name: 'Near East',
data: [2, 2, 2, 6, 13, 30, 46]
}, {
name: 'Asian CIS',
data: [2, 2, 2, 6, 13, 30, 46]
}, {
name: 'Russia',
data: [2, 2, 2, 6, 13, 30, 46]
}, {
name: 'East Europe',
data: [2, 2, 2, 6, 13, 30, 46]
}, {
name: 'Central Europe',
data: [2, 2, 2, 6, 13, 30, 46]
}, {
name: 'W. Europe - Nordic',
data: [2, 2, 2, 6, 13, 30, 46]
}, {
name: 'Nordic',
data: [2, 2, 2, 6, 13, 30, 46]
}, {
name: 'N. America',
data: [2, 2, 2, 6, 13, 30, 46]
}]
});
});
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<div id="grafica1" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
You can use the formatter like this :
tooltip: {
formatter: function() {
if(this.points[0].key < (this.points[0].series.xData.length -2)){
return false;
}
var s = [];
s.push(this.x);
this.points.forEach(function(point) {
s.push('<b>' + point.series.name + '</b>: ' + point.y);
});
return s;
},
split: true
},
Fiddle
Not the cleanest code (there still an error in the console) but it's work.
Edit
Following Deep 3015 comment, code and fiddle updated
You can wrap tooltip.prototype.renderSplit method to show the tooltip only if the points have certain x values.
Highcharts.wrap(Highcharts.Tooltip.prototype, 'renderSplit', function (p, labels, points) {
if (!this.options.showOnLastTwo) {
return p.call(this, labels, points)
}
const point = points[0]
if (point) {
const allowedXs = point.series.xData.slice(-2)
if (allowedXs.includes(point.x)) {
return p.call(this, labels, points)
}
}
this.destroy()
})
And set in tooltip options showOnLastTwo flag to true:
tooltip: {
split: true,
showOnLastTwo: true
},
The flag is not necessary but the wrap changes Highcharts globally so it will affect every chart - the flag will prevent this.
live example: http://jsfiddle.net/sLvekuht/1/
How could I decrease the cells height on x-axis as there are lots of empty spaces. I have used x-axis grouped-categories.js plug in. As you could see in the picture, the grey lines on x-Axis are too long and occupying too much space of my chart-container.
Below is the code which I have used for the chart:
xAxis: {
categories: [{
name: "Year 7",
categories: [1, 2, 3, 4]
}, {
name: "Year 8",
categories: [1, 2, 3, 4]
}, {
name: "Year 9",
categories: [1, 2, 3, 4]
}, {
name: "Year 10",
categories: [1, 2, 3, 4]
}, {
name: "Year 11",
categories: [1, 2, 3, 4]
}],
drawHorizontalBorders: false,
labels: {
rotation: 0,
groupedOptions: [{
y: 4,
style: {
fontSize: '5.5px',
padding: 0
}
}],
y: 7,
style: {
fontSize: '5.5px',
padding: 0
}
},
plotLines: [{
color: '#5DA06E',
width: 2,
value: l
}, {
color: '#5DA06E',
width: 2,
value: m
}],
min: m + 0.5,
max: l - 0.5
},
yAxis: [{
gridLineWidth: 0,
labels: {
enabled: false
},
title: {
text: null
},
min: 0,
max: 1000
},
{
gridLineWidth: 0,
title: {
text: false
},
labels: {
align: 'left',
x: 5,
formatter: function () {
var value = change[this.value];
return value !== 'undefined' ? value : this.value;
},
style: {
font: '8px arial'
}
},
tickPositions: [0, lines[0][1], lines[1][1], lines[2][1], lines[3][1], lines[4][1], lines[5][1], lines[6][1], lines[7][1], lines[8][1]],
gridLineColor: 'transparent',
opposite: true,
min: 0,
max: 1000
}],