How to create two x-axes label using chart.js - javascript

There is a way to create two label for y-axes. But how do you make a multiple x-axes label in chart.js? eg: example as in this picture:
How to group (two-level) axis labels

For v2 only (v3 see #LeeLenalee's answer)
This question has already been answered on github here
Here is a working JSFiddle
var ctx = $("#c");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["January;2015", "February;2015", "March;2015", "January;2016", "February;2016", "March;2016"],
datasets: [{
label: '# of Votes',
xAxisID:'xAxis1',
data: [12, 19, 3, 5, 2, 3]
}]
},
options:{
scales:{
xAxes:[
{
id:'xAxis1',
type:"category",
ticks:{
callback:function(label){
var month = label.split(";")[0];
var year = label.split(";")[1];
return month;
}
}
},
{
id:'xAxis2',
type:"category",
gridLines: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
ticks:{
callback:function(label){
var month = label.split(";")[0];
var year = label.split(";")[1];
if(month === "February"){
return year;
}else{
return "";
}
}
}
}],
yAxes:[{
ticks:{
beginAtZero:true
}
}]
}
}
});
<body>
<canvas id="c" width="400" height="300"></canvas>
<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.2/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
</body>

Updated accepted answer to also work with V3 since scale config has been changed:
var ctx = $("#c");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["January;2015", "February;2015", "March;2015", "January;2016", "February;2016", "March;2016"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3]
}]
},
options: {
scales: {
x: {
ticks: {
callback: function(label) {
let realLabel = this.getLabelForValue(label)
var month = realLabel.split(";")[0];
var year = realLabel.split(";")[1];
return month;
}
}
},
xAxis2: {
type: "category",
grid: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
ticks: {
callback: function(label) {
let realLabel = this.getLabelForValue(label)
var month = realLabel.split(";")[0];
var year = realLabel.split(";")[1];
if (month === "February") {
return year;
} else {
return "";
}
}
}
},
y: {
beginAtZero: true
}
}
}
});
<body>
<canvas id="c" width="400" height="300"></canvas>
<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.0/chart.js"></script>
</body>

var myChart = new Chart(ctx, {
type: "line",
data: {
datasets: [{
data: [20, 50, 100, 75, 25, 0],
label: "Left dataset",
// This binds the dataset to the left y axis
yAxisID: "left-y-axis",
}, {
data: [0.1, 0.5, 1.0, 2.0, 1.5, 0],
label: "Right dataset",
// This binds the dataset to the right y axis
yAxisID: "right-y-axis",
}],
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
},
options: {
scales: {
yAxes: [{
id: "left-y-axis",
type: "linear",
position: "left",
}, {
id: "right-y-axis",
type: "linear",
position: "right",
}],
},
},
});

okay maby a bit late ;)
how can we show the last tick in the second x-axis row?
with the code from above, we return a empty string.
i want to see the label of the last point.
ticks:{
callback:function(label){
var month = label.split(";")[0];
var year = label.split(";")[1];
if(month === "February"){
return year;
}else{
return ""; **<==== ???**
}
thx for the help.
EDIT
i change it a bit but not complete like i will
i don't want the label at 30 and 31 just the last day
w.a.w 31 label , 30 not a label
month ended at 30 => label
return month;
}else if
(Nbrday === "31"){
return month;
}else if
(Nbrday === "30"){
return month;
}
else{
// return month;
return "";

Related

Chart.js Update start date with button selector

Have been trying to add a range selector to my chart.
This is what I added to my php file.
<div id="range-selector">
<input type="button" id="30m" class="period ui-button" value="30m" />
<input type="button" id="1h" class="period ui-button" value="1h"/>
<input type="button" id="6h" class="period ui-button" value="6h"/>
<input type="button" id="12h" class="period ui-button" value="12h"/>
<input type="button" id="24h" class="period ui-button" value="24h"/>
</div>
this is my chart.js file
var fanspeedlabels = [], fanspeed = [], temp = [];
function updateFanSpeedData() {
function formatDate(itemdate) {
return moment(itemdate).format("MMM Do HH:mm");
}
$.ajax({
url: 'api.php?getFanSpeed24hrs&PHP',
dataType: 'json'
}).done(function (results) {
results.forEach(function (packet) {
if (fanspeedlabels.indexOf(formatDate(packet.start_time)) === -1) {
fanspeedlabels.push(formatDate(packet.start_time));
fanspeed.push(parseFloat(packet.fanspeed));
temp.push(parseFloat(packet.temp));
}
});
fanspeedChart.update();
fanspeeddata = results;
});
}
setInterval(function () {
// console.log('updateFanSpeedData');
// updateFanSpeedData();
}, 6000);
var fanspeedChartctx = document.getElementById("fanspeedChart");
var newfanspeed = fanspeed + "%";
var fanspeedChart = new Chart(fanspeedChartctx, {
type: 'line',
data: {
labels: fanspeedlabels,
datasets: [{
label: 'FanSpeed',
data: fanspeed,
backgroundColor: 'rgb(60, 141, 188)',
fill: false,
borderColor: 'rgb(60, 141, 188)',
borderWidth: 1,
cubicInterpolationMode: 'monotone',
yAxisID: "y-axis-1"
},
{
label: 'Temp',
data: temp,
backgroundColor: 'rgba(255, 99, 132, 1)',
fill: false,
borderColor: 'rgba(255,99,132,1)',
borderWidth: 1,
yAxisID: "y-axis-1"
}
]
},
options: {
hover: {
animationDuration: 0 // duration of animations when hovering an item
},
responsive: true,
maintainAspectRatio: false,
legend: {
display: false
},
scales: {
yAxes: [{
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
display: true,
position: "left",
id: "y-axis-1",
ticks : {
min: 0,
max: 100
}
},
{
type: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
display: true,
position: "right",
id: "y-axis-2",
ticks : {
min: 0,
max: 100
}
}
],
xAxes: [
{
display: true,
scaleLabel: {
display: true
},
ticks: {
maxRotation: 0,
minRotation: 0
}
}
]
},
tooltips: {
enabled: true,
mode: "x-axis",
intersect: false,
callbacks: {
label: function(t, d) {
var xLabel = d.datasets[t.datasetIndex].label;
var yLabel = t.yLabel;
if(t.datasetIndex === 0) {
return 'Fan Speed: ' + yLabel.toFixed(0) + '%';
}
else if (t.datasetIndex === 1) {
return 'CPU Temp: ' + yLabel.toFixed(0) + '°C';
}
}
}
}
}
});
$(".period").click( function() {
var period = this.id;
minValue = new Date();
switch(period){
case "30m":
minValue.setMinutes(minValue.getMinutes() - 30);
break;
case "1h":
minValue.setHours(minValue.getHours() - 1);
break;
case "6h":
minValue.setHours(minValue.getHours() - 6);
break;
case "12h":
minValue.setHours(minValue.getHours() - 12);
break;
case "24h":
minValue.setHours(minValue.getHours() - 24);
break;
default:
minValue
}
var startdate = moment(minValue).format("MMM Do HH:mm");
fanspeedChart.options.scales.xAxes[0].ticks.max = startdate;
fanspeedChart.update();
});
updateFanSpeedData();
The buttons work, Did have a console.log and the dates change currently but it doesn't update the chart with the current start point.
Any Help would be great, Starting to feel like I'm Going backwards.
Update something I Found
As I have dug a little deeper. The code seems to work, but because the "startdate" does not equal the exact date from the database it does not work. If by some chance there is the same date in the database it works. Is there a way I can make it so if any date higher than this date add. Dates show up as "Feb 16 19:50". if it sets min as "Feb 16 19:49" it will not work. Have tried suggestedMin aswell
As I worked out, as the min didn't match any dates from my database, needed a way to get the closest date to the date provided.
Added this and all working great now.
let closest = Infinity;
fanspeedlabels.forEach(function(d) {
const date = new Date(d);
if (date >= now && (date < new Date(closest) || date < closest)) {
closest = d;
}
});
fanspeedChart.options.scales.xAxes[0].ticks.min = closest;
fanspeedChart.update();

Chart.js : How I change the x axes ticks labels alignment in any sizes?

How can I move my labels on my x axes in between another x axes label. Nothing seems to work and I was unable to find anything on the docs. Is there a workaround? I'm using line chart time series.
https://www.chartjs.org/samples/latest/scales/time/financial.html
Currently, with the code I have its generating the figure below:
var cfg = {
elements:{
point: {
radius: 4
}
},
data: {
datasets: [
{
label: 'vsy',
backgroundColor: color(window.chartColors.red).alpha(0.5).rgbString(),
borderColor: window.chartColors.red,
data: firstData,
type: 'line',
pointRadius: 2,
fill: false,
lineTension: 0,
borderWidth: 2
},
{
label: 'de vsy',
backgroundColor: color(window.chartColors.blue).alpha(0.5).rgbString(),
borderColor: window.chartColors.blue,
data: dataMaker(15),
type: 'line',
pointRadius: 2,
fill: false,
lineTension: 0,
borderWidth: 2
}
],
},
options: {
animation: {
duration: 0
},
scales: {
xAxes: [{
type: 'time',
distribution: 'series',
offset: true,
time: {
unit: 'month',
displayFormats: {
month: 'MMM'
}
},
ticks: {
autoSkip: true,
autoSkipPadding: 75,
sampleSize: 100
},
}],
yAxes: [{
gridLines: {
drawBorder: false
}
}]
},
tooltips: {
intersect: false,
mode: 'index',
}
}
};
This is what I have now:
I want the labels on the x-axis to be on center instead of below the y axis grid line.
Thanks to uminder, with his comment it solves the issue but now I have a conflicting tooltip which lie on a same grid. When I hover to april line first point it shows me mar 30 which lies just above it and vice versa.
I fixed it by changing the mode to nearest but why is it activating the another point?
The option you're looking for is offsetGridLines.
If true, grid lines will be shifted to be between labels.
xAxes: [{
...
gridLines: {
offsetGridLines: true
}
In most cases, this produces the expected result. Unfortunately it doesn't work for time axes as documented in Chart.js issue #403. Thanks to Antti Hukkanen, there exists a workaround.
Please have a look at below runnable code snippet to see how it works.
function generateData() {
var unit = 'day';
function randomNumber(min, max) {
return Math.random() * (max - min) + min;
}
function randomPoint(date, lastClose) {
var open = randomNumber(lastClose * 0.95, lastClose * 1.05).toFixed(2);
var close = randomNumber(open * 0.95, open * 1.05).toFixed(2);
return {
t: date.valueOf(),
y: close
};
}
var date = moment().subtract(1, 'years');
var now = moment();
var data = [];
for (; data.length < 600 && date.isBefore(now); date = date.clone().add(1, unit).startOf(unit)) {
data.push(randomPoint(date, data.length > 0 ? data[data.length - 1].y : 30));
}
return data;
}
var TimeCenterScale = Chart.scaleService.getScaleConstructor('time').extend({
getPixelForTick: function(index) {
var ticks = this.getTicks();
if (index < 0 || index >= ticks.length) {
return null;
}
// Get the pixel value for the current tick.
var px = this.getPixelForOffset(ticks[index].value);
// Get the next tick's pixel value.
var nextPx = this.right;
var nextTick = ticks[index + 1];
if (nextTick) {
nextPx = this.getPixelForOffset(nextTick.value);
}
// Align the labels in the middle of the current and next tick.
return px + (nextPx - px) / 2;
},
});
// Register the scale type
var defaults = Chart.scaleService.getScaleDefaults('time');
Chart.scaleService.registerScaleType('timecenter', TimeCenterScale, defaults);
var cfg = {
data: {
datasets: [{
label: 'CHRT - Chart.js Corporation',
backgroundColor: 'red',
borderColor: 'red',
data: generateData(),
type: 'line',
pointRadius: 0,
fill: false,
lineTension: 0,
borderWidth: 2
}]
},
options: {
animation: {
duration: 0
},
scales: {
xAxes: [{
type: 'timecenter',
time: {
unit: 'month',
stepSize: 1,
displayFormats: {
month: 'MMM'
}
},
gridLines: {
offsetGridLines: true
}
}],
yAxes: [{
gridLines: {
drawBorder: false
}
}]
},
tooltips: {
intersect: false,
mode: 'index'
}
}
};
var chart = new Chart('chart1', cfg);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart1" height="90"></canvas>
For chartJs v3 you can use offset property:
scales: {
x: {
grid: {
offset: true
}
},
...
}

Chart displaying values outside the min and max ranges (Chart.js)

When I set a min and max value for my chart it still displays all the MIN values squished up to the left of my graph, but the max values disappear as they should.
HTML
<div id="graph">
<canvas id="line-chart" width="400" height="225"></canvas>
</div>
<button id="12hours">12 Hours</button>
<button id="24hours">24 Hours</button>
JS
function displayGraph(object)
{
timestamp = getDataForGraph(object, 'timestamp');
temp1 = getDataForGraph(object, 'temp1');
temp2 = getDataForGraph(object, 'temp2');
temp3 = getDataForGraph(object, 'temp3');
var mychart = new Chart(document.getElementById("line-chart"), {
type: 'line',
data: {
labels: timestamp,
datasets: [{
data: temp1,
label: "Temp 1",
borderColor: "#ff0000",
fill: false
}, {
data: temp2,
label: "Temp 2",
borderColor: "#3bff00",
fill: false
}, {
data: temp3,
label: "Temp 3",
borderColor: "#00edff",
fill: false
}
]
},
options: {responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [{
ticks: {
fontSize: 5
},
type: 'time',
time: {
unit: 'hour',
displayFormats: {
hour: 'HH:mm:ss'
}
}
}],
yAxes: [{
ticks: {
fontSize: 5
}
}]
}
}
});
$('#12hours').off().on('click', function () {
mychart.options.scales.xAxes[0].time.min = '2018-10-29 08:00:00';
mychart.options.scales.xAxes[0].time.max = '2018-10-29 20:00:00';
mychart.update();
});
$('#24hours').off().on('click', function () {
mychart.options.scales.xAxes[0].time.min = '2018-10-29 00:00:00';
mychart.options.scales.xAxes[0].time.max = '2018-10-29 23:59:59';
mychart.update();
});
}
Current output when using min and max values.
would like to get rid of all the values before 08:00 that are showing up on the left hand side of the axes.
What it looks like when max and min are placed
What it looks like with no max or min placed

Make Chart.js horizontal bar labels multi-line

Just wondering if there is any way to set the horizontal bar labels for y-axis using chart.js. Here is how I set up the chart:
<div class="box-body">
<canvas id="chart" style="position: relative; height: 300px;"></canvas>
</div>
Javascript:
var ctx = document.getElementById('chart').getContext("2d");
var options = {
layout: {
padding: {
top: 5,
}
},
responsive: true,
animation: {
animateScale: true,
animateRotate: true
},
};
var opt = {
type: "horizontalBar",
data: {
labels: label,
datasets: [{
data: price,
}]
},
options: options
};
if (chart) chart.destroy();
chart= new Chart(ctx, opt);
chart.update();
As you all can see, the first and third labels are too long and cut off. Is there a way to make the label multi-line?
If you want to have full control over how long labels are broken down across lines you can specify the breaking point by providing labels in a nested array. For example:
var chart = new Chart(ctx, {
...
data: {
labels: [["Label1 Line1:","Label1 Line2"],["Label2 Line1","Label2 Line2"]],
datasets: [{
...
});
You can use the following chart plugin :
plugins: [{
beforeInit: function(chart) {
chart.data.labels.forEach(function(e, i, a) {
if (/\n/.test(e)) {
a[i] = e.split(/\n/);
}
});
}
}]
add this followed by your chart options
ᴜꜱᴀɢᴇ :
add a new line character (\n) to your label, wherever you wish to add a line break.
ᴅᴇᴍᴏ
var chart = new Chart(ctx, {
type: 'horizontalBar',
data: {
labels: ['Jan\n2017', 'Feb', 'Mar', 'Apr'],
datasets: [{
label: 'BAR',
data: [1, 2, 3, 4],
backgroundColor: 'rgba(0, 119, 290, 0.7)'
}]
},
options: {
scales: {
xAxes: [{
ticks: {
beginAtZero: true
}
}]
}
},
plugins: [{
beforeInit: function(chart) {
chart.data.labels.forEach(function(e, i, a) {
if (/\n/.test(e)) {
a[i] = e.split(/\n/);
}
});
}
}]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>
<canvas id="ctx"></canvas>

Chart.js stacked and grouped horizontalBar chart

I've been trying to display somewhat complex data on my webpage and chose chart.js to do so.
Therefor I need to group multiple stacked bars horizontally.
I already found this fiddle for "normal" bars but couldn't quite change it to work with horizontalBar yet.
Stackoverflow question: Chart.js stacked and grouped bar chart
The original Fiddle (http://jsfiddle.net/2xjwoLq0/) has
Chart.defaults.groupableBar = Chart.helpers.clone(Chart.defaults.bar);
And I just replaced the .bar everywhere in the code with .horizontalBar (well knowing that this won't make the cut).
Chart.defaults.groupableBar = Chart.helpers.clone(Chart.defaults.horizontalBar);
Since that didn't quite work, I tried adding the second stacked modifier as suggested for horizontal bars here:
Horizontal stacked bar chart with chart.js and flipped the functions for X and Y calculation (calculateBarY/calculateBarX)
Which quite work either because the stacks won't get merged onto each other correctly.
http://jsfiddle.net/2xjwoLq0/3/
I would appreciate if anyone could help me out on this one.
Looking for something similar, I took a look on example you gave, and decide to write something.
Rather than trying to fix the code or reusing the 'groupableBar', I get Chart.js code from Chart.controllers.horizontalBar and rewrite some part in functions calculateBarY, calculateBarHeight.
Just reused the getBarCount function from your example.
Chart.defaults.groupableHBar = Chart.helpers.clone(Chart.defaults.horizontalBar);
Chart.controllers.groupableHBar = Chart.controllers.horizontalBar.extend({
calculateBarY: function(index, datasetIndex, ruler) {
var me = this;
var meta = me.getMeta();
var yScale = me.getScaleForId(meta.yAxisID);
var barIndex = me.getBarIndex(datasetIndex);
var topTick = yScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo);
topTick -= me.chart.isCombo ? (ruler.tickHeight / 2) : 0;
var stackIndex = this.getMeta().stackIndex;
if (yScale.options.stacked) {
if(ruler.datasetCount>1) {
var spBar=ruler.categorySpacing/ruler.datasetCount;
var h=me.calculateBarHeight(ruler);
return topTick + (((ruler.categoryHeight - h) / 2)+ruler.categorySpacing-spBar/2)+(h+spBar)*stackIndex;
}
return topTick + (ruler.categoryHeight / 2) + ruler.categorySpacing;
}
return topTick +
(ruler.barHeight / 2) +
ruler.categorySpacing +
(ruler.barHeight * barIndex) +
(ruler.barSpacing / 2) +
(ruler.barSpacing * barIndex);
},
calculateBarHeight: function(ruler) {
var returned=0;
var me = this;
var yScale = me.getScaleForId(me.getMeta().yAxisID);
if (yScale.options.barThickness) {
returned = yScale.options.barThickness;
}
else {
returned= yScale.options.stacked ? ruler.categoryHeight : ruler.barHeight;
}
if(ruler.datasetCount>1) {
returned=returned/ruler.datasetCount;
}
return returned;
},
getBarCount: function () {
var stacks = [];
// put the stack index in the dataset meta
Chart.helpers.each(this.chart.data.datasets, function (dataset, datasetIndex) {
var meta = this.chart.getDatasetMeta(datasetIndex);
if (meta.bar && this.chart.isDatasetVisible(datasetIndex)) {
var stackIndex = stacks.indexOf(dataset.stack);
if (stackIndex === -1) {
stackIndex = stacks.length;
stacks.push(dataset.stack);
}
meta.stackIndex = stackIndex;
}
}, this);
this.getMeta().stacks = stacks;
return stacks.length;
}
});
var data = {
labels: ["January", "February", "March"],
datasets: [
{
label: "Dogs",
backgroundColor: "rgba(255,0,0,0.2)",
data: [20, 10, 25],
stack: 1,
xAxisID: 'x-axis-0',
yAxisID: 'y-axis-0'
},
{
label: "Cats",
backgroundColor: "rgba(255,255,0,0.2)",
data: [70, 85, 65],
stack: 1,
xAxisID: 'x-axis-0',
yAxisID: 'y-axis-0'
},
{
label: "Birds",
backgroundColor: "rgba(0,255,255,0.2)",
data: [10, 5, 10],
stack: 1,
xAxisID: 'x-axis-0',
yAxisID: 'y-axis-0'
},
{
label: ":-)",
backgroundColor: "rgba(0,255,0,0.2)",
data: [20, 10, 30],
stack: 2,
xAxisID: 'x-axis-1',
yAxisID: 'y-axis-0'
},
{
label: ":-|",
backgroundColor: "rgba(0,0,255,0.2)",
data: [40, 50, 20],
stack: 2,
xAxisID: 'x-axis-1',
yAxisID: 'y-axis-0'
},
{
label: ":-(",
backgroundColor: "rgba(0,0,0,0.2)",
data: [60, 20, 20],
stack: 2,
xAxisID: 'x-axis-1',
yAxisID: 'y-axis-0'
},
]
};
var ctx = document.getElementById("myChart").getContext("2d");
new Chart(ctx, {
type: 'groupableHBar',
data: data,
options: {
scales: {
yAxes: [{
stacked: true,
type: 'category',
id: 'y-axis-0'
}],
xAxes: [{
stacked: true,
type: 'linear',
ticks: {
beginAtZero:true
},
gridLines: {
display: false,
drawTicks: true,
},
id: 'x-axis-0'
},
{
stacked: true,
position: 'top',
type: 'linear',
ticks: {
beginAtZero:true
},
id: 'x-axis-1',
gridLines: {
display: true,
drawTicks: true,
},
display: false
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.min.js"></script>
<canvas id="myChart"></canvas>
Also put example on jsfiddle here: https://jsfiddle.net/b7gnron7/4/
Code is not strongly tested, you might found some bugs especially if you try to display only one stacked group (use horizontalBar instead in this case).
Your post is a little bit old... not sure that you still need a solution, but it could be useful for others ^_^

Categories