I'm trying to shorten my chart legends, so I'm using the beforeRender plugin to change the text of the legend. However, once the chart is drawn, it seems to keep the original label size (see below snippet, legends are offset to the left instead of in the centre and if you click on orange, it toggles one of the earlier items).
The same happens if I try in beforeDraw and if I use beforeInit or beforeLayout, the legends don't seem to have been created yet.
Is there any way to get the chart to redraw the legends with the new text? Or change the legend text so that it is rendered properly?
var ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx, {
type: 'pie',
data: {
labels: ['red - some other text that appears after', 'blue - some other text that appears after', 'green - some other text that appears after', 'orange - some other text that appears after'],
datasets: [{
data: [4, 2, 10, 3],
backgroundColor: ['red', 'blue', 'green', 'orange'],
}],
},
options: {
responsive: true,
},
plugins: [{
beforeRender: chart => {
chart.legend.legendItems.forEach(label => {
const labelParts = label.text.split(' - ');
const newLabel = label;
newLabel.text = labelParts[0];
return newLabel;
});
},
}],
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.bundle.js"></script>
<div style="width: 100%">
<canvas id="canvas"></canvas>
</div>
A better approach would be to use a custom generateLabels function like so:
var ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx, {
type: 'pie',
data: {
labels: ['red - some other text that appears after', 'blue - some other text that appears after', 'green - some other text that appears after', 'orange - some other text that appears after'],
datasets: [{
data: [4, 2, 10, 3],
backgroundColor: ['red', 'blue', 'green', 'orange'],
}],
},
options: {
responsive: true,
legend: {
labels: {
generateLabels: function(chart) {
const data = chart.data;
if (data.labels.length && data.datasets.length) {
return data.labels.map(function(label, i) {
const meta = chart.getDatasetMeta(0);
const style = meta.controller.getStyle(i);
return {
text: label.split(' - ')[0],
fillStyle: style.backgroundColor,
strokeStyle: style.borderColor,
lineWidth: style.borderWidth,
hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden,
// Extra data used for toggling the correct item
index: i
};
});
}
return [];
}
}
}
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.bundle.js"></script>
<div style="width: 100%">
<canvas id="canvas"></canvas>
</div>
Related
Lets say I have a Donut chart with 5 items in data like this
const data = {
labels: ['E-commerce', 'Enterprise', 'Green', 'Grey', 'Purple'],
datasets: [
{
label: '# of Votes',
data: [12, 19, 3, 5, 3],
backgroundColor: ['#C07CC3', '#9C3848', '#9DDBAD', '#ADA8B6', '#606EDA'],
borderWidth: 1,
},
],
}
I don't want it to show all the legends as I don't have space or whatever reason
How can I hide the Green and purple in this example?
I mean only from legends not from chart
I see two easy ways, how you could approach this problem:
(I personally I would use the second option below, it is more configurable, but it needs just abit of coding)
You can simply delete the labels you want to delete from the labels- array, and they won't show up in the legend.
But keep in mind you would have to change the order of the data and backgroundColor arrays, to match this change.
Here a short demo:
const data = {
labels: ['E-commerce', 'Enterprise', 'Grey'], // <-- just remove the unwanted labels
datasets: [{
data: [12, 19, 5, 3, 3], // <-- reorder
backgroundColor: ['#C07CC3', '#9C3848', '#ADA8B6', '#9DDBAD', '#606EDA'], // <-- reorder
borderWidth: 1,
}],
};
const config = {
type: 'doughnut',
data: data,
options: {
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
usePointStyle: true,
},
}
},
}
};
new Chart(
document.getElementById('chart'),
config
);
<script src="//cdn.jsdelivr.net/npm/chart.js"></script>
<div class="chart" style="height:184px; width:350px;">
<canvas id="chart" ></canvas>
</div>
Better and cleaner (but some coding is needed), you can filter out label-items, you don't want to display, with the function array filter. (details can be found in the documentation)
UPDATED Alternative Version Demo:
here only the Top 3 labels (limiting the amount of labels with the variable maxLabelsToShow) will be shown (sort order is descending, but changing this is would be easy)
function getLabelsOnlyTopX(num, data, labels){
let selectedLabels = []
//we don't want to alter the order
let helperData = [...data];
//sort in descending order
helperData.sort( (a,b) => b-a);
//get top X Values
helperData = helperData.slice(0, num);
//get index for the data
let indexes = data.map( (value, index) => ({value,index}) ).filter(item => helperData.some(n1 => n1 == item.value))
//slecet only labels with the correct index
selectedLabels = labels.filter((value, index) => indexes.some( n => n.index == index))
// just be sure that a maximum of num labels are sent
return selectedLabels.slice(0, num);
}
let maxLabelsToShow = 3;
let serverData = [12, 19, 3, 5, 3]
let labels = ['E-commerce', 'Enterprise', 'Green', 'Grey', 'Purple'];
// Calling the newly created function
let showOnly = getLabelsOnlyTopX(maxLabelsToShow, serverData, labels);
const data = {
labels: labels,
datasets: [{
data: serverData,
backgroundColor: ['#C07CC3', '#9C3848',
'#9DDBAD', '#ADA8B6', '#606EDA'],
borderWidth: 1,
}],
};
const config = {
type: 'doughnut',
data: data,
options: {
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
usePointStyle: true,
/* FILTER function */
filter: function(item, chart) {
return showOnly.indexOf( item.text) > -1;
}
},
}
},
}
};
new Chart(
document.getElementById('chart'),
config
);
<script src="//cdn.jsdelivr.net/npm/chart.js"></script>
<div class="chart" style="height:184px; width:350px;">
<canvas id="chart" ></canvas>
</div>
In my code below, I am able to draw a Treemap and also display the tag in each tree cell. But I want to add some custom text next to my tag,
Like say I want the word 'Language' to appear in each tree map cell. I tried the formatter options, But its not working for the version of chart.js I am using. Kindly provide me a solution for the specific version I am using. Since I have multiple other charts defined and changing the version would cause issues.
var topTags = [
{tag:'android',num:42657},{tag:'reactjs',num:38844},{tag:'php',num:34381},{tag:'sql',num:29996},
];
var canvas = document.getElementById("treemap");
var ctx = canvas.getContext("2d");
var chart = window.chart = new Chart(ctx, {
type: "treemap",
data: {
datasets: [{
tree: topTags,
key: "num",
groups: ['tag'],
spacing: 0.5,
borderWidth: 1.5,
fontColor: "black",
borderColor: "grey"
}]
},
options: {
maintainAspectRatio: false,
legend: { display: false },
tooltips: { enabled: false }
}
});
CHART.JS AND TREEMAP VERSION :
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-chart-treemap#0.2.3"></script>
The formatter option only got added in version 2 of the treemap chart type, the scriptable option is only available in version 1 so the best way for you to get the extra text is by editing your original data array first and adding the text to it that way like so:
let topTags = [{
tag: 'android',
num: 42657
}, {
tag: 'reactjs',
num: 38844
}, {
tag: 'php',
num: 34381
}, {
tag: 'sql',
num: 29996
}, ];
topTags = topTags.map(e => ({
num: e.num,
tag: `Lang: ${e.tag}`
}))
const canvas = document.getElementById("chart");
const ctx = canvas.getContext("2d");
const chart = window.chart = new Chart(ctx, {
type: "treemap",
data: {
datasets: [{
tree: topTags,
key: "num",
groups: ['tag'],
spacing: 0.5,
borderWidth: 1.5,
fontColor: "black",
borderColor: "grey",
}]
},
options: {
maintainAspectRatio: false,
legend: {
display: false
},
tooltips: {
enabled: false
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-chart-treemap#0.2.3"></script>
<canvas id="chart"></canvas>
I have a bar chart that always shows 4 bars. The bars are coloured dynamically. It looks like the coloured box takes its colour from the first data. I would like to use the colour from the 4th (last) data value. Maybe the options:plugins:legend:label:sort function helps but I don't understand what it does.
options
const options = {
scales: {
x: {
grid: {
display: false,
color: 'rgba(0,0,0)'
}
},
y: {
display: false,
min: 0,
max: 4
},
},
plugins: {
legend: {
position: 'bottom'
}
}
}
So I don't know if I can change the data that the box color comes from, or if there is a config option somewhere where I can change it manually.
You can use the generateLabels function as described here.
Please take a look at below runnable sample code and see how it works.
new Chart('myChart', {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow'],
datasets: [{
label: 'My Dataset',
data: [300, 50, 100],
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56']
}]
},
options: {
responsive: false,
plugins: {
legend: {
labels: {
generateLabels: chart => {
let ds = chart.data.datasets[0];
let color = ds.backgroundColor[ds.backgroundColor.length - 1];
return [{
datasetIndex: 0,
text: ds.label,
fillStyle: color,
strokeStyle: color
}];
}
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.min.js"></script>
<canvas id="myChart" height="180"></canvas>
Following up with #uminder's answer, if you want to keep the hide/show chart and the line-through style after clicking on the legend, you can add the following line:
options: {
responsive: false,
plugins: {
legend: {
labels: {
generateLabels: chart => {
let ds = chart.data.datasets[0];
let color = ds.backgroundColor[ds.backgroundColor.length - 1];
return [{
datasetIndex: 0,
text: ds.label,
fillStyle: color,
strokeStyle: color,
+ hidden: !chart.isDatasetVisible(0)
}];
}
}
}
}
}
Is it possible to have multiple titles in Chart JS? https://www.chartjs.org/
At the current state I use the title as a description below the chart. I also need a title on top of the chart. Is it possible?
An alternative for my case would be if there was another way to implement a description at the bottom and keep the title as top title.
var chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
title: {
display: true,
text: 'Custom Chart Title',
position: 'bottom',
}
}
});
You can use the Plugin Core API. It offers different hooks that may be used for executing custom code. In below code snippet, I use the afterDraw hook to draw a title on the canvas. That way you can draw as many titles as you want, define them of different style each and place them wherever it suits you.
Please note that inside chart options, I also defined some layout padding. This prevents the title from overlapping the chart bars.
layout: {
padding: {
top: 40
}
}
Chart.plugins.register({
afterDraw: chart => {
var ctx = chart.chart.ctx;
ctx.save();
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = "18px Arial";
ctx.fillStyle = "gray";
ctx.fillText('Top Title', chart.chart.width / 2, 20);
ctx.restore();
}
});
new Chart(document.getElementById('myChart'), {
type: 'bar',
data: {
labels: ['A', 'B', 'C', 'D'],
datasets: [{
data: [10, 12, 8, 6],
backgroundColor: ['red', 'blue', 'green', 'orange']
}]
},
options: {
layout: {
padding: {
top: 40
}
},
title: {
display: true,
text: 'Custom Chart Title',
position: 'bottom',
},
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="100"></canvas>
I'm trying to build a graph with chart.js and i would like to replace the labels with logtype small images.
Is it possible ? Can't find the way to do it.
Thanks.
This is my chart options:
var horizontalBarChartData3 = {
labels: ["mark1", "mark2", "mark3", "mark4","mark5"],
datasets: [
{
label: 'USA',
backgroundColor: "rgba(196,196,196,0.5)",
data: [0.07,0.02,0.26,0.68,0.21]
},
{
hidden: false,
label: 'EUROPA',
backgroundColor: "rgba(125,125,125,0.5)",
data: [0.09,0.02,0.31,0.74,0.23]
},
{
label: 'CHINA',
backgroundColor: "rgba(165,157,4,0.5)",
data: [0.15,0.02,0.38,0.99,0.27]
}]
};
Adding an image to the labels is a tricky adjustment, and will require fiddling with chart.js code, as it isn't natively supported. My best suggestion is to avoid attempts to add your images to the labels themselves.
Best solution for using icons would be one of:
Use images of your own, but place them around the chart in proper positions, and if needed, refer to em in labels.
Use an icon font to insert icons as characters of text. Here's an example using Font Awesome, using your data (based off of this fiddle):
var randomScalingFactor = function () {
return Math.round(Math.random() * 100);
};
var barChartData = {
pointLabelFontFamily: "'FontAwesome'",
labels: ["\uf000", "\uf001", "\uf002", "\uf003", "\uf004"],
datasets: [
{
label: 'USA',
backgroundColor: "rgba(196,196,196,0.5)",
data: [0.07,0.02,0.26,0.68,0.21]
},
{
hidden: false,
label: 'EUROPA',
backgroundColor: "rgba(125,125,125,0.5)",
data: [0.09,0.02,0.31,0.74,0.23]
},
{
label: 'CHINA',
backgroundColor: "rgba(165,157,4,0.5)",
data: [0.15,0.02,0.38,0.99,0.27]
}]
};
var ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx).Bar(barChartData, {
responsive: true,
scaleFontFamily: "'FontAwesome'"
});
<script src="http://chartjs.org/assets/Chart.min.js"></script>
<link rel="stylesheet" type="text/css" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css">
<div style="width: 75%">
<span style="font-family: FontAwesome"></span>
<canvas id="canvas" height="450" width="600"></canvas>
</div>