I need some help with Chart.js. I am wondering if it is possible to make the labels for the graphs smaller or have some kind of overflow effect.
As you can see from the picture, it is very overwhelming and makes the graphs small.
By overflow effect I mean something like this:
The option to be able to scroll.
#user2057925 is right. You should use an HTML legend through a plugin. The code is given in Chart.js documentation and all you have to do is tweaking styles a bit (especially overflow).
Here is a full example:
const ctx = document.getElementById('myChart');
const getOrCreateLegendList = (chart, id) => {
const legendContainer = document.getElementById(id);
let listContainer = legendContainer.querySelector('ul');
if (!listContainer) {
listContainer = document.createElement('ul');
// ========================= TWEAK THAT =========================
listContainer.style.display = 'flex';
listContainer.style.flexDirection = 'column';
listContainer.style.margin = 'auto';
listContainer.style.padding = 0;
listContainer.style.width = '150px';
listContainer.style.height = '60px';
listContainer.style.overflow = 'scroll'; // <--- DO NOT FORGET THIS
// ==================================================
legendContainer.appendChild(listContainer);
}
return listContainer;
};
const htmlLegendPlugin = {
id: 'htmlLegend',
afterUpdate(chart, args, options) {
const ul = getOrCreateLegendList(chart, options.containerID);
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = chart.options.plugins.legend.labels.generateLabels(chart);
items.forEach(item => {
const li = document.createElement('li');
li.style.alignItems = 'center';
li.style.cursor = 'pointer';
li.style.display = 'flex';
li.style.flexDirection = 'row';
li.style.marginTop = '5px';
li.style.marginLeft = '10px';
li.onclick = () => {
const {type} = chart.config;
if (type === 'pie' || type === 'doughnut') {
// Pie and doughnut charts only have a single dataset and visibility is per item
chart.toggleDataVisibility(item.index);
} else {
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
}
chart.update();
};
// Color box
const boxSpan = document.createElement('span');
boxSpan.style.background = item.fillStyle;
boxSpan.style.borderColor = item.strokeStyle;
boxSpan.style.borderWidth = item.lineWidth + 'px';
boxSpan.style.display = 'inline-block';
boxSpan.style.height = '20px';
boxSpan.style.marginRight = '10px';
boxSpan.style.width = '20px';
// Text
const textContainer = document.createElement('p');
textContainer.style.color = item.fontColor;
textContainer.style.margin = 0;
textContainer.style.padding = 0;
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
const text = document.createTextNode(item.text);
textContainer.appendChild(text);
li.appendChild(boxSpan);
li.appendChild(textContainer);
ul.appendChild(li);
});
}
};
new Chart(ctx, {
type: 'line',
data: {
labels: ['Label 1', 'Label 2', 'Label 3'],
datasets: [{
label: 'Dataset 1',
data: [10, 20, 15]
}, {
label: 'Dataset 2',
data: [7, 3, 11]
}, {
label: 'Dataset 3',
data: [6, 8, 12]
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
},
plugins: {
htmlLegend: {
containerID: 'legend-container'
},
legend: {
display: false
}
}
},
plugins: [htmlLegendPlugin]
});
.chart-container {
position: relative;
width: 500px;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<div class="chart-container">
<div id="legend-container"></div>
<canvas id="myChart"></canvas>
</div>
Related
I try to make a link if a onclick event on a doughnut chart slice happens. My datasources are 3 arrays with labels, value, and the id for the url.
HTML:
<canvas id="pie-chart" style='display: none;'></canvas>
<!-- Php Arrays to JS -> PIE-CHARTDATA -->
<script type="text/javascript">
var chartIds = [[12,14,17,18]];
var chartValues = [[208.09,296.86,634.975,972.808]];
var chartLabels = [["BTC","AAPL","MSFT","ETH"]];
</script>
JS:
if (chartValues.length != 0 ) {
document.getElementById("pie-chart").style.display= "block";
}
Chart.register(ChartDataLabels);
var chartValuesInt = [];
length = chartValues[0].length;
for (var i = 0; i < length; i++)
chartValuesInt.push(parseInt(chartValues[0][i]));
var data = [{
data: chartValuesInt,
chartIds,
backgroundColor: [
"#f38000",
"#5f44f5",
"#333333",
],
borderColor: "#000"
}];
var options = {
borderWidth: 4,
hoverOffset: 6,
plugins: {
legend: {
display: false
},
tooltip: {
enabled: false,
},
datalabels: {
formatter: (value, ctx) => {
let sum = 0;
let dataArr = ctx.chart.data.datasets[0].data;
dataArr.map(data => {
sum += data;
});
let percentage = (value*100 / sum).toFixed(2)+"%";
return [ctx.chart.data.labels[ctx.dataIndex],
percentage,
'$' + value ] ;
},
textAlign: 'center',
color: '#fff',
borderRadius: 50,
padding:10,
labels: {
title: {
font: {
weight: 'bold',
size: '16px'
}
},
}
}
},
options:{
onClick: (e, activeEls) => {
let datasetIndex = activeEls[0].datasetIndex;
let dataIndex = activeEls[0].index;
let datasetLabel = e.chart.data.datasets[datasetIndex].label;
let value = e.chart.data.datasets[datasetIndex].data[dataIndex];
console.log("In click", datasetLabel, value);
//link to url with:[chartIds]
}
}
};
//IMAGE CENTER
const image = new Image();
image.src = 'img/pie-home2.png';
const plugin = {
id: 'custom_canvas_background_image',
beforeDraw: (chart) => {
if (image.complete) {
const ctx = chart.ctx;
const {top, left, width, height} = chart.chartArea;
const x = left + width / 2 - image.width / 2;
const y = top + height / 2 - image.height / 2;
ctx.drawImage(image, x, y);
} else {
image.onload = () => chart.draw();
}
}
};
var ctx = document.getElementById("pie-chart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: chartLabels[0],
datasets: data,
chartIds
},
options: options,
plugins: [plugin],
});
why does the onclick didn't work ?
how do i get the id with the right index from the slice where the event happens?
I searched already, but couldn't find a answer to these 2 questions.
You onClick function does not work because you define an options object within your options object and put the onClick in there. This is not supported. When you remove the inner options layer it will work:
const options = {
borderWidth: 4,
hoverOffset: 6,
plugins: {
legend: {
display: false
},
tooltip: {
enabled: false,
},
datalabels: {
formatter: (value, ctx) => {
let sum = 0;
let dataArr = ctx.chart.data.datasets[0].data;
dataArr.map(data => {
sum += data;
});
let percentage = (value * 100 / sum).toFixed(2) + "%";
return [ctx.chart.data.labels[ctx.dataIndex],
percentage,
'$' + value
];
},
textAlign: 'center',
color: '#fff',
borderRadius: 50,
padding: 10,
labels: {
title: {
font: {
weight: 'bold',
size: '16px'
}
},
}
}
},
onClick: (e, activeEls) => {
let datasetIndex = activeEls[0].datasetIndex;
let dataIndex = activeEls[0].index;
let datasetLabel = e.chart.data.datasets[datasetIndex].label;
let value = e.chart.data.datasets[datasetIndex].data[dataIndex];
console.log("In click", datasetLabel, value);
//link to url with:[chartIds]
}
};
In my code below, I am able to draw a Treemap and also display the tag in each tree cell. But the text is overflowing the tile if it's a long word
I need to ensure the word stays in the tile even If it means putting .... after certain characters. How can I achieve them? Please have a look at the version of chart.js and Treemap I am using before providing the solution. Thanks a lot :)
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>
I solved it by splitting the text into multiple lines, starting a new line whenever the max width would be exceeded.
const chart = new Chart(context, {
type: 'treemap',
data: {
datasets: [
{
/* ... */
labels: {
display: true,
formatter(ctx: TreemapScriptableContext) {
if (ctx.type !== 'data') {
return;
}
return splitLabelToFit(ctx.raw["_data"].label, ctx.raw.w*0.9, ctx);
}
}
}
],
},
});
function splitLabelToFit(label: string, maxWidth: number, ctx: TreemapScriptableContext) {
const words = label.split(' ');
const lines = [];
let currentLine = '';
for (let i = 0; i < words.length; i++) {
const word = words[i];
const newLine = currentLine + ' ' + word;
const width = ctx.chart.ctx.measureText(newLine).width;
if (width < maxWidth) {
currentLine = newLine;
} else {
lines.push(currentLine);
currentLine = word;
}
}
lines.push(currentLine);
return lines;
}
Good day to all:
Recently I have started working with Vue.js(2.6.12) + Vuetify(2.3.10) and Chart.js(3.0.2). So I'm a newbie (again).
I have created a componenent which wraps the Bar chart that Chart.js allows us to create. Here it's a picture of it:
The only two things that I want to change are the little box that comes near to the legend title and some grid x lines.
In the case of the little legend box is red. I would like that It's aligned with the blue color of the legend title text. As I show you in this picture:
Finally I want to include some color (pink) in the X axis:
I'm unable to achieve this. I have regarded and follow the official documentation and nothing works :S.
Create the function for painting some axis in colors: https://www.chartjs.org/docs/3.0.2/samples/scale-options/grid.html
Change the colors of the legend: https://www.chartjs.org/docs/3.0.2/configuration/legend.html#legend-label-configuration
In the case of the legend title box I have noticed that always takes the color of the first element.
In the case of the axis, the function supported by chart.js doesn't work for me. It doesn't print me the x axis at all.
Things that I have tried:
Upgrade to chart.js 3.3.0 but I got an error like this: ""
The reason I was using 3.0.2 it's because it's the only version which
is working to me from the 3.0.0 versions.
Downgrade to 2.9.3/4. I wasn't able to change the color of the box or the axis lines, but the rest worked fine.
Use a wrapper: https://vue-chartjs.org/. It didn't work
Code of the whole component:
<template>
<div class="container pa-3" fill-height fluid style="width: 100%">
<!-- We create the chart -->
<canvas id="myChart1" />
</div>
</template>
<script>
import Chart from "chart.js/auto";
export default {
name: "Chart",
components: {},
props: {},
data: () => ({
ctx: null,
myChart: null,
type: "bar",
data: {
labels: ["a", "b", "c", "d"],
datasets: [
{
data: [1, 2, 3, 4],
backgroundColor: ["#c30", "#e37609", "#ffda05", "#fffb05"],
},
],
},
options: {
plugins: {
legend: {
display: true,
labels: {
color: "#00a3fb",
},
},
},
scales: {
},
},
}),
methods: {
createChart: function () {
// destroy the previous graph
if (this.myChart != null) this.myChart.destroy();
// create a new one
this.ctx = document.getElementById("myChart1");
this.myChart = new Chart(this.ctx, {
type: this.type,
data: this.data,
options: this.options,
});
this.myChart.render();
},
},
destroyed() {},
mounted() {
this.createChart();
},
watch: {},
};
</script>
<style scoped>
</style>
For using it, you should:
Import it in the section
Declare it in the component section
Call it by <NameOfComponetGiven/> tag
Any help would be quite aprecciated.
Thank you very much.
To customize the legend box color you will need to use a custom HTML legend, there you can specify it with CSS, for the pink grid lines you can use the scriptable options. For both see example:
const getOrCreateLegendList = (chart, id) => {
const legendContainer = document.getElementById(id);
let listContainer = legendContainer.querySelector('ul');
if (!listContainer) {
listContainer = document.createElement('ul');
listContainer.style.display = 'flex';
listContainer.style.flexDirection = 'row';
listContainer.style.margin = 0;
listContainer.style.padding = 0;
legendContainer.appendChild(listContainer);
}
return listContainer;
};
const htmlLegendPlugin = {
id: 'htmlLegend',
afterUpdate(chart, args, options) {
const ul = getOrCreateLegendList(chart, options.containerID);
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = chart.options.plugins.legend.labels.generateLabels(chart);
items.forEach(item => {
const li = document.createElement('li');
li.style.alignItems = 'center';
li.style.cursor = 'pointer';
li.style.display = 'flex';
li.style.flexDirection = 'row';
li.style.marginLeft = '10px';
li.onclick = () => {
const {
type
} = chart.config;
if (type === 'pie' || type === 'doughnut') {
// Pie and doughnut charts only have a single dataset and visibility is per item
chart.toggleDataVisibility(item.index);
} else {
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
}
chart.update();
};
// Color box
const boxSpan = document.createElement('span');
boxSpan.style.background = options.legendBoxColor || item.fillStyle;
boxSpan.style.borderColor = item.strokeStyle;
boxSpan.style.borderWidth = item.lineWidth + 'px';
boxSpan.style.display = 'inline-block';
boxSpan.style.height = '20px';
boxSpan.style.marginRight = '10px';
boxSpan.style.width = '20px';
// Text
const textContainer = document.createElement('p');
textContainer.style.color = options.legendTextColor || item.fontColor;
textContainer.style.margin = 0;
textContainer.style.padding = 0;
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
const text = document.createTextNode(item.text);
textContainer.appendChild(text);
li.appendChild(boxSpan);
li.appendChild(textContainer);
ul.appendChild(li);
});
}
};
const options = {
type: 'bar',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderWidth: 1,
backgroundColor: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"]
}]
},
options: {
scales: {
x: {
grid: {
color: (line) => ((line.index === 2 || line.index === 3) ? 'pink' : 'rgba(0,0,0,0.1)'),
lineWidth: (line) => ((line.index === 2 || line.index === 3) ? 6 : 1)
}
}
},
plugins: {
htmlLegend: {
// ID of the container to put the legend in
containerID: 'legendContainer',
legendBoxColor: 'blue',
legendTextColor: 'blue'
},
legend: {
display: false,
}
}
},
plugins: [htmlLegendPlugin]
}
const ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
<body>
<div id="legendContainer"></div>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.0.2/chart.js"></script>
</body>
I am having 3 different charts on my web page for which I am using Chartjs. The problem occurs when I hover over one of the charts it starts showing old data. I am creating chart on a HTML button click. I checked few answers on stackoverflow (for eg. destroy()) but that is not working for me. Below is the function for chart. please guide me regarding this.
<script>
function dailyPrd1() {
var pl_type1 = "";
var pl_sl1 = "";
var date1="";
pl_type1 = plant_type1.options[plant_type1.selectedIndex].innerHTML;
//alert(pl_type1);
pl_sl1 = plant_select1.options[plant_select1.selectedIndex].innerHTML;
//alert(pl_sl1);
date1 = document.getElementById('date2').value;
//alert(date1);
var pl2 = "";
pl2 = pl_type1 + '-' + pl_sl1;
var obj2 = "";
var hrs1 = [];
var prod1 = [];
var colr1 = [];
var req2 = new XMLHttpRequest();
var config_string2 = '<%=ConfigurationManager.AppSettings["serverip11"].ToString() %>' + pl_sl1 + "/" + pl_type1 + "/" + date1;
req2.open("GET", config_string2, true);
req2.send();
req2.overrideMimeType("application/json");
req2.onload = function () {
obj2 = JSON.parse(this.response);
obj2 = JSON.parse(obj2);
var len12 = 0;
len12 = obj2.day_list.length;
for (i = 0; i < len12; i++) {
hrs1.push(obj2.day_list[i].day);
}
var speedCanvas2 = document.getElementById("myChart3");
Chart.defaults.global.defaultFontFamily = "Lato";
Chart.defaults.global.defaultFontSize = 16;
var chartOptions2 = {
responsive: true,
scales: {
xAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Days'
}
}],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Value in cu.m'
}
}]
},
legend: {
display: true,
position: 'top',
labels: {
boxWidth: 80,
fontColor: 'black'
}
}
};
var speedData2 = {
labels: hrs1,
// datasets: [dataFirst, dataSecond]
};
var lineChart2 = new Chart(speedCanvas2, {
type: 'bar',
data: speedData2,
options: chartOptions2
});
var iti1 = 0;
iti1 = obj2.prod_qty.length;
var aaa = 'Pl 1'
for (j = 0; j < iti1; j++) {
prod1.push(obj2.prod_qty[j].tot_prod);
}
addData(lineChart2, pl2, getRandomColor(), prod1);
}
}
</script>
After you change your data you should update your chart with chartVariable.update().
I made a JSBin which explains you how to use it.
The important function for you is the last in the code, addDataButton() which gets triggered by a button click. In this function I add new data and update my chart after that.
Instead of chartVariable and chart you should use lineChart2 in your case.
Complete code:
let numberOfDataCounter = 0 // Current data counter
const numberOfDataAtBeginning = 4 // data number to start with
const weekdays = ["Su", "Mo", "Du", "We", "Th", "Fr", "Sa"]
function randomNumber(){
let randomNumber = Math.floor(Math.random()*100)
return randomNumber
}
let chartData = {
label: [],
data: []
}
function addData (){
chartData.label.push(weekdays[numberOfDataCounter % 7])
chartData.data.push(randomNumber())
numberOfDataCounter++
}
// Fill chart with data at beginning
while (numberOfDataAtBeginning>numberOfDataCounter) {
addData()
}
let data = {
labels: chartData.label,
datasets: [{
label: "Label",
data: chartData.data
}]
}
let chart = new Chart(document.getElementById("chart"), {
type: 'line',
data: data,
options: {
scales: {
yAxes: [{
ticks: {
min: 0,
max: 100
}
}]
}
}
});
function addDataButton(){
addData()
chart.update()
}
I have a selected list where i want different charts to appear, but when i hover the new chart the old chart is bugging in and out. I have tryed to delete the chart but im not sure what to do.
I have added new values to the array stat using the if and selectedIndex. Would really appreciate someone helping me:). I have see other people saying to use . destroy(); but i cant make it work.
var item = document.getElementById("item");
var ctx = document.getElementById("chart");
var btn = document.getElementById("btn");
var stat = [];
function check() {
var Index = item.selectedIndex;
if(Index === 1){
stat = [3,15.3,21.8,1.4,4.1,0.8,15.2,26.8,4.1];
tabell();
}else if(Index === 2){
stat =[1.4,5.5,16.6,1.4,7.5,1.2,30.3,28.9,4.2];
tabell();
}
}
function show() {
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["player1", "player2", "player3", "player4", "player5", "player6","player7", "player8","player9"],
datasets: [{
label: 'table thing',
data:stat ,
backgroundColor: ['#650012', '#a80014', '#e10028', '#b0e13e', '#23b121', '#ccd535', '#248e26', '#3c96bc', '#1721bc'],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
}
btn.onclick = function () {
check();
};
var item = document.getElementById("item");
var ctx = document.getElementById("chart");
var btn = document.getElementById("btn");
var stat = [];
function check() {
var Index = item.selectedIndex;
if(Index === 1){
stat = [3,15.3,21.8,1.4,4.1,0.8,15.2,26.8,4.1];
tabell();
}else if(Index === 2){
stat =[1.4,5.5,16.6,1.4,7.5,1.2,30.3,28.9,4.2];
tabell();
}
}
function show() {
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ["player1", "player2", "player3", "player4", "player5", "player6","player7", "player8","player9"],
datasets: [{
label: 'Av 100 stemmer',
data:stat ,
backgroundColor: ['#650012', '#a80014', '#e10028', '#b0e13e', '#23b121', '#ccd535', '#248e26', '#3c96bc', '#1721bc'],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
}
btn.onclick = function () {
check();
};