considering the following example:
$(document).ready(function() {
var ctx = document.getElementById('mycanvas').getContext('2d');
var chart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ["CL", "ML", "Spl.L", "PD", "Other Permissions"],
datasets: [{
label: "My First dataset",
backgroundColor: ['#F0CB8C', '#EE97A1', '#A9D5D4', '#E8A3D7', '#CFA3FD'],
data: [7, 3, 3, 4, 8],
}]
},
options: {
legend: {
position: 'right'
}
}
});
})
example
is there a way to have the data on the right of the single label?
like here:
You can add custom labels as advised by #LeeLenalee's solution
and here is your workin code :
$(document).ready(function() {
var ctx = document.getElementById('mycanvas').getContext('2d');
var chart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ["CL", "ML", "Spl.L", "PD", "Other Permissions"],
datasets: [{
label: "My First dataset",
backgroundColor: ['#F0CB8C', '#EE97A1', '#A9D5D4', '#E8A3D7', '#CFA3FD'],
data: [7, 3, 3, 4, 8],
}]
},
options: {
legend: {
labels: {
generateLabels: (chart) => {
const datasets = chart.data.datasets;
return datasets[0].data.map((data, i) => ({
text: `${chart.data.labels[i]} ${data}`,
fillStyle: datasets[0].backgroundColor[i],
}))}
}
}
}
});
})
.chart-container {
width: 280px;
height: 280px;
}
<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.7.1/Chart.min.js"></script>
<div class="chart-container">
<canvas id="mycanvas"></canvas>
</div>
Related
(Please note: There are lots of answers for v2, this is for v3)
I'm trying to setup tooltips label and title for a doughnut chart.
Code:
//Create the donut chart
donut = new Chart('questions_positivity_donut', {
type: 'doughnut',
data: {
labels: ["Positive", "Other"],
datasets: [{
label: 'Sentiment',
data: [user_context.state.avg_joy, (1-user_context.state.avg_joy)],
backgroundColor: ['#a9a9a9','#f2f2f2']
}]
},
options: {
cutout: "70%",
plugins: {
legend: {
display: false
},
maintainAspectRatio: false,
responsive: true,
tooltip: {
callbacks: {
label: function(context) {
let label = new Intl.NumberFormat('en-US', {style: 'percent', minimumFractionDigits: 0, maximumFractionDigits: 0}).format(context.formattedValue);
return label;
},
title: function(context) {
let title = context.parsed.x;
return title;
}
},
displayColors: false
}
}
}
});
The label now works, and displays the value of the data, but the title is returning blank, instead of returning the label of the data ("Positive" or "Other").
How can I return the correct title in the tooltip.callback?
Example: "Positive 35%" and "Other 65%"
If you log the context you could see its an array containing objects, with the default interaction mode you are using it only contains a single item so you can select that one and then access the label attribute on it like so:
var options = {
type: 'doughnut',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"]
}]
},
options: {
plugins: {
tooltip: {
callbacks: {
label: function(context) {
let label = new Intl.NumberFormat('en-US', {
style: 'percent',
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(context.formattedValue);
return label;
},
title: function(context) {
let title = context[0].label;
return title;
}
},
}
}
}
}
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/3.5.1/chart.js"></script>
</body>
With a duplicate value in the data labels - Is there a way to draw the bubble on the first or second duplicate value in the bubble chart?
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
var ctx = document.getElementById("myChart");
var options = {responsive: true,
maintainAspectRatio: false,
};
var mixedChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["1", "2", "1", "4"], //Same Value on First and Third Position
datasets: [
//Lines
{
label: "First_Line",
type: "line",
borderColor: "#8e5ea2",
data: [5,10,7,12],
fill: false
}, {
label: "Second_Line",
type: "line",
borderColor: "#3e95cd",
data: [1,4,15,6],
fill: false
},
//Bubbles
{
label: "Bubble_One",
type: "bubble",
backgroundColor: "#8e5ea2",
data: [{ x: "2", y: 10, r: 15}],
},
{
label: "Bubble_Two",
type: "bubble",
backgroundColor: "#3e95cd",
backgroundColorHover: "#3e95cd",
data: [{x: ???, y: 6, r: 15}] //First "1" or Second "1" possible?
}
]
},
options: options
});
</script>
Something like "1"[0] unfortunately does not work.
You can not do it directly afaik but you can put an extra part in the label and filter it out of there with the callbacks for the tooltip and the axis:
<canvas id="myChart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
var ctx = document.getElementById("myChart");
var options = {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
offset: false,
ticks: {
callback: function(val) {
return this.getLabelForValue(val).split('-')[0]
}
}
}
},
plugins: {
tooltip: {
callbacks: {
label: (ttItem) => {
if (ttItem.dataset.type === "bubble") {
return `${ttItem.label}: (${ttItem.raw.x.split('-')[0]},${ttItem.raw.y})`
} else if (ttItem.dataset.type === "line") {
return `${ttItem.dataset.label}: ${ttItem.parsed.y}`
}
},
title: (ttItem) => (ttItem[0].label.split('-')[0])
}
}
}
};
var mixedChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["1-1", "2", "1-2", "4"], //Same Value on First and Third Position
datasets: [
//Lines
{
label: "First_Line",
type: "line",
borderColor: "#8e5ea2",
data: [5, 10, 7, 12],
fill: false
}, {
label: "Second_Line",
type: "line",
borderColor: "#3e95cd",
data: [1, 4, 15, 6],
fill: false
},
//Bubbles
{
label: "Bubble_One",
type: "bubble",
backgroundColor: "#8e5ea2",
data: [{
x: "2",
y: 10,
r: 15
}],
},
{
label: "Bubble_Two",
type: "bubble",
backgroundColor: "#3e95cd",
backgroundColorHover: "#3e95cd",
data: [{
x: "1-2",
y: 6,
r: 15
}] //First "1" or Second "1" possible?
}
]
},
options: options
});
</script>
I am trying to use ChartJS latest version and I'm getting this problem, it shows four Y Axes, three on the left. Here is my code:
<html>
<body>
<canvas id="QGL_Chart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.1.1/dist/chart.min.js" integrity="sha256-lISRn4x2bHaafBiAb0H5C7mqJli7N0SH+vrapxjIz3k=" crossorigin="anonymous"></script>
<script>
var ctx = document.getElementById("QGL_Chart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'Left dataset',
yAxisID: 'first-y-axis',
data: [1,2,3,4]
},
{
label: 'right dataset',
yAxisID: 'second-y-axis',
data: [4,3,2,1]
}],
labels: [1,2,3,4]
},
options: {
scales: {
'left-y-axis': {
type: 'linear',
position: 'left'
},
'right-y-axis': {
type: 'linear',
position: 'right'
}
}
}
});
</script>
</body>
</html>
If you run the snippet, you see that there are three Y Axes by the left, what might be the problem? Thanks.
This is because the object names of your scales become the ID, and since you try to map your datasets to non existent scale ID's it creates them for you extra. If you make the axisId prop in your dataset the same as you gave the object name it will work as inteded, see snippet:
<html>
<body>
<canvas id="QGL_Chart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.1.1/dist/chart.min.js" integrity="sha256-lISRn4x2bHaafBiAb0H5C7mqJli7N0SH+vrapxjIz3k=" crossorigin="anonymous"></script>
<script>
var ctx = document.getElementById("QGL_Chart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'Left dataset',
yAxisID: 'left-y-axis',
data: [1, 2, 3, 4]
},
{
label: 'right dataset',
yAxisID: 'right-y-axis',
data: [4, 3, 2, 1]
}
],
labels: [1, 2, 3, 4]
},
options: {
scales: {
'left-y-axis': {
type: 'linear',
position: 'left'
},
'right-y-axis': {
type: 'linear',
position: 'right'
}
}
}
});
</script>
</body>
</html>
Is it possible to get 2 separate types of chart (in this case bar and line) for the same dataset, but under one label?
As an example, this is my chart:
const ctx = document.querySelector('#chart-container').getContext('2d');
new Chart(ctx, getConfig());
function getConfig() {
return {
type: 'bar',
data: {
labels: [ "Mar-20", "Apr-20" ],
datasets: [{
type: "line",
label: "en-US",
borderColor: "#8c856f",
data: [ 2, 3 ],
borderWidth: 2,
fill: false
}, {
type: "bar",
label: "en-US",
backgroundColor: "#beb391",
data: [ 2, 3 ]
}, {
type: "line",
label: "sv-SE",
borderColor: "#b3cbaa",
data: [ 1, 2 ],
borderWidth: 2,
fill: false
}, {
type: "bar",
label: "sv-SE",
backgroundColor: "#683e3a",
data: [ 1, 2 ]
}]
},
options: {
scales: {
yAxes: [{
ticks: {
min : 0
}
}]
}
}
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart-container"></canvas>
As you can see, I have 4 datasets: 2 for each language (en & se). It all works perfectly fine except the legend labels are generated for each dataset (2 x "en-US", 2 x "sv-SE"), while I would like to show only the unique labels, essentially using the values from the main "labels" property, instead of the one inside each dataset.
Is it possible, and how can I do it?
You can filter out the labels for the line series via:
options: {
legend: {
labels: {
filter: function(item, chart) {
return chart.datasets[item.datasetIndex].type === 'bar';
}
},
onClick: function(e, legendItem) {
let chart = this.chart;
let index = legendItem.datasetIndex;
let visible = !chart.getDatasetMeta(index).hidden;
chart.data.datasets.forEach((dataset, i) => {
if (dataset.label === legendItem.text) {
chart.getDatasetMeta(i).hidden = visible;
}
});
chart.update();
}
}
}
This has been adapted from: Is it possible in chartjs to hide certain dataset legends?
For toggling the similar datasets, I followed this example.
Demo
const ctx = document.querySelector('#chart-container').getContext('2d');
new Chart(ctx, getConfig());
function getConfig() {
return {
type: 'bar',
data: {
labels: ["Mar-20", "Apr-20"],
datasets: [{
type: "line",
label: "en-US",
borderColor: "#8c856f",
data: [2, 3],
borderWidth: 2,
fill: false
}, {
type: "bar",
label: "en-US",
backgroundColor: "#beb391",
data: [2, 3]
}, {
type: "line",
label: "sv-SE",
borderColor: "#b3cbaa",
data: [1, 2],
borderWidth: 2,
fill: false
}, {
type: "bar",
label: "sv-SE",
backgroundColor: "#683e3a",
data: [1, 2]
}]
},
options: {
legend: {
labels: {
filter: function(item, chart) {
return chart.datasets[item.datasetIndex].type === 'bar';
}
},
// Override hide/show for multiple series with same label.
onClick: function(e, legendItem) {
let chart = this.chart;
let index = legendItem.datasetIndex;
let visible = !chart.getDatasetMeta(index).hidden;
chart.data.datasets.forEach((dataset, i) => {
if (dataset.label === legendItem.text) {
chart.getDatasetMeta(i).hidden = visible;
}
});
chart.update();
}
},
scales: {
yAxes: [{
ticks: {
min: 0
}
}]
}
}
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.js"></script>
<canvas id="chart-container"></canvas>
Alternatively, you could create a plugin... This makes it so that you can configure each individual series.
var hideLegendItemsPlugin = {
beforeInit: function(chartInstance) {
if (chartInstance.options.hideLegendItemsPlugin) {
Object.assign(chartInstance.options.legend, {
labels : {
filter : (item, chart) => {
return chart.datasets[item.datasetIndex].hideLegendItem !== true;
}
},
onClick: function(e, legendItem) {
let chart = this.chart;
let index = legendItem.datasetIndex;
let visible = !chart.getDatasetMeta(index).hidden;
chart.data.datasets.forEach((dataset, i) => {
if (dataset.label === legendItem.text) {
chart.getDatasetMeta(i).hidden = visible;
}
});
chart.update();
}
});
}
}
};
Chart.pluginService.register(hideLegendItemsPlugin);
const ctx = document.querySelector('#chart-container').getContext('2d');
new Chart(ctx, getConfig());
function getConfig() {
return {
type: 'bar',
data: {
labels: ["Mar-20", "Apr-20"],
datasets: [{
type: "line",
label: "en-US",
borderColor: "#8c856f",
data: [2, 3],
borderWidth: 2,
fill: false,
hideLegendItem: true // Hide it!
}, {
type: "bar",
label: "en-US",
backgroundColor: "#beb391",
data: [2, 3]
}, {
type: "line",
label: "sv-SE",
borderColor: "#b3cbaa",
data: [1, 2],
borderWidth: 2,
fill: false,
hideLegendItem: true // Hide it!
}, {
type: "bar",
label: "sv-SE",
backgroundColor: "#683e3a",
data: [1, 2]
}]
},
options: {
hideLegendItemsPlugin : true, // Enable the plugin
scales: {
yAxes: [{
ticks: {
min: 0
}
}]
}
}
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart-container"></canvas>
If you want to have the chart figure-out label uniqueness, you can build a set of series names behind the scenes.
I also added an option to specify priority of series type i.e. bar, line, etc... If you do not specify a priority, the last appearance of the label will take priority. This works in your example, because the bar series are after the line series. Obviously this can be tweaked a bit.
var hideLegendItemsPlugin = {
beforeInit: function(chartInstance) {
let pluginOptions = chartInstance.options.hideLegendItemsPlugin;
if (pluginOptions) {
if (pluginOptions.priority) {
const labelMap = chartInstance.data.datasets.reduce((ret, dataset) => {
return Object.assign(ret, {
[dataset.label] : (ret[dataset.label] || []).concat(dataset.type)
});
}, {});
chartInstance.options.legend.labels.filter = (item, chart) => {
let dataset = chart.datasets[item.datasetIndex];
if (dataset.type !== pluginOptions.priority) {
if (labelMap[dataset.label].includes(pluginOptions.priority)) {
return false;
}
}
return true;
};
} else {
// Default prioritization is the last index (appearance) of that label.
const labelMap = chartInstance.data.datasets.reduce((ret, dataset, index) => {
return Object.assign(ret, { [dataset.label] : index });
}, {});
chartInstance.options.legend.labels.filter = (item, chart) => {
let dataset = chart.datasets[item.datasetIndex];
return item.datasetIndex === labelMap[dataset.label];
};
}
chartInstance.options.legend.onClick = function(e, legendItem) {
let chart = this.chart;
let index = legendItem.datasetIndex;
let visible = !chart.getDatasetMeta(index).hidden;
chart.data.datasets.forEach((dataset, i) => {
if (dataset.label === legendItem.text) {
chart.getDatasetMeta(i).hidden = visible;
}
});
chart.update();
};
}
}
};
Chart.pluginService.register(hideLegendItemsPlugin);
const ctx = document.querySelector('#chart-container').getContext('2d');
new Chart(ctx, getConfig());
function getConfig() {
return {
type: 'bar',
data: {
labels: ["Mar-20", "Apr-20"],
datasets: [{
type: "line",
label: "en-US",
borderColor: "#8c856f",
data: [2, 3],
borderWidth: 2,
fill: false
}, {
type: "bar",
label: "en-US",
backgroundColor: "#beb391",
data: [2, 3]
}, {
type: "line",
label: "sv-SE",
borderColor: "#b3cbaa",
data: [1, 2],
borderWidth: 2,
fill: false,
hideLegendItem: true
}, {
type: "bar",
label: "sv-SE",
backgroundColor: "#683e3a",
data: [1, 2]
}]
},
options: {
// This could also be simply `true` instead of an object.
hideLegendItemsPlugin : {
priority : 'bar'
},
scales: {
yAxes: [{
ticks: {
min: 0
}
}]
}
}
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart-container"></canvas>
Currently I have this doughnut chart
I am trying to change tooltip style like this
var ctx = document.getElementById('mycanvas').getContext('2d');
var chart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ["AA", "BB", "CC", "DD", "EE"],
datasets: [{
label: "My First dataset",
backgroundColor: ['yellow', 'pink', 'red', '#E8A3D7', '#CFA3FD'],
data: [7, 3, 3, 4, 8],
}]
},
options: {
legend: {
position: 'right',
labels: {
boxWidth: 12
}
}
}
});
But I couldn't find a way.Does anybody know if this is possible?
https://jsfiddle.net/xm80qzoo/2/