I want to display all of the points on my chart from the data I get, but I don't want to display all the labels for them, because then the chart is not very readable. I was looking for it in the docs, but couldn't find any parameter that would limit this.
I don't want to take only three labels for example, because then the chart is also limited to three points. Is it possible?
I have something like that right now:
If I could just leave every third-fourth label, it would be great. But I found absolutely nothing about labels options.
Try adding the options.scales.xAxes.ticks.maxTicksLimit option:
xAxes: [{
type: 'time',
ticks: {
autoSkip: true,
maxTicksLimit: 20
}
}]
For concreteness, let's say your original list of labels looks like:
["0", "1", "2", "3", "4", "5", "6", "7", "8"]
If you only want to display every 4th label, filter your list of labels so that every 4th label is filled in, and all others are the empty string (e.g. ["0", "", "", "", "4", "", "", "", "8"]).
For anyone looking to achieve this on Chart JS V2 the following will work:
var options = {
scales: {
xAxes: [{
afterTickToLabelConversion: function(data){
var xLabels = data.ticks;
xLabels.forEach(function (labels, i) {
if (i % 2 == 1){
xLabels[i] = '';
}
});
}
}]
}
}
Then pass the options variable as usual into a:
myLineChart = new Chart(ctx, {
type: 'line',
data: data,
options: options
});`
UPDATE:
I'v updated my fork with the latest pull (as of Jan 27, 2014) from NNick's Chart.js master branch.
https://github.com/hay-wire/Chart.js/tree/showXLabels
ORIGINAL ANSWER:
For those still facing this issue, I forked Chart.js a while back to solve the same problem. You can check it out on:
https://github.com/hay-wire/Chart.js/tree/skip-xlabels => Older branch! Check showXLabels branch for latest pull.
How to use:
Applicable to bar chart and line chart.
User can now pass a { showXLabels: 10 } to display only 10 labels (actual displayed labels count might be a bit different depending on the number of total labels present on x axis, but it will still remain close to 10 however)
Helps a lot when there is a very large amount of data. Earlier, the graph used to look devastated due to x axis labels drawn over each other in the cramped space. With showXLabels, user now has the control to reduce the number of labels to whatever number of labels fit good into the space available to him.
See the attached images for a comparison.
Without showXLabels option:
With { showXLabels: 10 } passed into option:
Here's some discussion on it:
https://github.com/nnnick/Chart.js/pull/521#issuecomment-60469304
For Chart.js 3.3.2, you can use #Nikita Ag's approach with a few changes. You can check the documentation. Put ticks in xAxis in scales. Example:
...
options: {
scales: {
xAxis: {
ticks: {
maxTicksLimit: 10
}
}
}
}
...
for axis rotation
use this:
scales: {
xAxes: [
{
// aqui controlas la cantidad de elementos en el eje horizontal con autoSkip
ticks: {
autoSkip: true,
maxRotation: 0,
minRotation: 0
}
}
]
}
In Chart.js 3.2.0:
options: {
scales: {
x: {
ticks: {
maxTicksLimit: 10
}
}
}
}
According to the chart.js github issue #12. Current solutions include:
Use 2.0 alpha (not production)
Hide x-axis at all when it becames too crowd (cannot accept at all)
manually control label skip of x-axis (not in responsive page)
However, after a few minutes, I thinks there's a better solution.
The following snippet will hide labels automatically. By modify xLabels with empty string before invoke draw() and restore them after then. Even more, re-rotating x labels can be applied as there's more space after hiding.
var axisFixedDrawFn = function() {
var self = this
var widthPerXLabel = (self.width - self.xScalePaddingLeft - self.xScalePaddingRight) / self.xLabels.length
var xLabelPerFontSize = self.fontSize / widthPerXLabel
var xLabelStep = Math.ceil(xLabelPerFontSize)
var xLabelRotationOld = null
var xLabelsOld = null
if (xLabelStep > 1) {
var widthPerSkipedXLabel = (self.width - self.xScalePaddingLeft - self.xScalePaddingRight) / (self.xLabels.length / xLabelStep)
xLabelRotationOld = self.xLabelRotation
xLabelsOld = clone(self.xLabels)
self.xLabelRotation = Math.asin(self.fontSize / widthPerSkipedXLabel) / Math.PI * 180
for (var i = 0; i < self.xLabels.length; ++i) {
if (i % xLabelStep != 0) {
self.xLabels[i] = ''
}
}
}
Chart.Scale.prototype.draw.apply(self, arguments);
if (xLabelRotationOld != null) {
self.xLabelRotation = xLabelRotationOld
}
if (xLabelsOld != null) {
self.xLabels = xLabelsOld
}
};
Chart.types.Bar.extend({
name : "AxisFixedBar",
initialize : function(data) {
Chart.types.Bar.prototype.initialize.apply(this, arguments);
this.scale.draw = axisFixedDrawFn;
}
});
Chart.types.Line.extend({
name : "AxisFixedLine",
initialize : function(data) {
Chart.types.Line.prototype.initialize.apply(this, arguments);
this.scale.draw = axisFixedDrawFn;
}
});
Please notice that clone is an external dependency.
i had a similar type of issue, and was given a nice solution to my specific issue show label in tooltip but not in x axis for chartjs line chart. See if this helps you
you can limit at as
scales: {
x: {
ticks: {
// For a category axis, the val is the index so the lookup via getLabelForValue is needed
callback: function(val, index) {
// Hide the label of every 2nd dataset
return index % 5 === 0 ? this.getLabelForValue(val) : '';
},
}
}
}
this will skip 4 labels and set the 5th one only.
you can use the following code:
xAxes: [{
ticks: {
autoSkip: true,
maxRotation: 90
}
}]
You may well not need anything with this new built-in feature.
A built-in label auto-skip feature detects would-be overlapping ticks and labels and removes every nth label to keep things displaying normally. https://www.chartjs.org/docs/latest/axes/
To set a custom number of ticks regardless of your chartsjs version:
yAxes: [{
ticks: {
stepSize: Math.round((Math.max.apply(Math, myListOfyValues) / 10)/5)*5,
beginAtZero: true,
precision: 0
}
}]
10 = the number of ticks
5 = rounds tick values to the nearest 5. All your y values will have the same step size.
Similar will work for xAxes too.
This answer works like a charm.
If you are wondering about the clone function, try this one:
var clone = function(el){ return el.slice(0); }
In the Chart.js file, you should find (on line 884 for me)
var Line = function(...
...
function drawScale(){
...
ctx.fillText(data.labels[i], 0,0);
...
If you just wrap that one line call to fillText with if ( i % config.xFreq === 0){ ... }
and then in chart.Line.defaults add something line xFreq : 1 you should be able to start using xFreq in your options when you call new Chart(ctx).Line(data, options).
Mind you this is pretty hacky.
Related
I started working with highcharts. Now a problem I am facing is that the bulletcharts can’t be used for a dataset that is in descending order.
For example say we want to plot the ranking of a few companies. In this case the ranking is better the lower it gets. So a Rank 10 is better than a Rank 15 and so on.
In this case I want my graph to get reversed. The target would be lets say 10. The min value would start from say 100 to 0. So you can see how this isnt possible.
P.S: I know the
reverse: true/false
property. But that simply flips the graph and I don’t want that/need that.
Thanks world.
You can add a second y-axis with the same extremes as the first one, but reversed. Next, hide the first y-axis, calculate mocked y and target values and finally, set a custom formatter function for tooltip to show original values.
const MAX = 300;
const TARGET = 50;
const Y = 25;
Highcharts.chart('container1', {
chart: {
inverted: true,
type: 'bullet',
},
yAxis: [{
visible: false,
min: 0,
max: MAX
}, {
min: 0,
max: MAX,
reversed: true,
...
}],
tooltip: {
formatter: function(tooltip) {
const point = this.point;
return `<span style='color: ${this.color}'>●</span> ${this.series.name}: <b>${point.originalY}</b>. Target: <b>${point.originalTarget}</b><br/>`
}
},
series: [{
data: [{
y: MAX - Y,
target: MAX - TARGET,
originalY: Y,
originalTarget: TARGET
}]
}]
});
Live demo: https://jsfiddle.net/BlackLabel/ve8hosd3/
API Reference: https://api.highcharts.com/highcharts/tooltip.formatter
In the highcharts example above suppose I have 100 series in Bananas which is 1 right now and just one series in Apples ,and if there is a lot of empty space between Bananas and Oranges can we reduce the spacing between them ?
The reason is if there are 100 series in Bananas due to space constraint every line gets overlapped even though there is extra space available between Bananas and Apples . Also is it possible to remove "Oranges" if it doesnt have any series at all and accomodate only series from "Bananas"?
Categories functionality works only for constant tick interval equaled to 1. What you're trying to achieve is having a different space reserved for every category. That means that tick interval has to be irregular.
Unfortunately Highcharts doesn't provide a property to do that automatically - some coding and restructuring the data is required:
All the points have specified x position (integer value)
xAxis.grouping is disabled and xAxis.pointRangeis 1
Following code is used to define and position the labels:
events: {
render: function() {
var xAxis = this.xAxis[0];
for (var i = 0; i < xAxis.tickPositions.length; i++) {
var tickPosition = xAxis.tickPositions[i],
tick = xAxis.ticks[tickPosition],
nextTickPosition,
nextTick;
if (!tick.isLast) {
nextTickPosition = xAxis.tickPositions[i + 1];
nextTick = xAxis.ticks[nextTickPosition];
tick.label.attr({
y: (new Number(tick.mark.d.split(' ')[2]) + new Number(nextTick.mark.d.split(' ')[2])) / 2 + 3
});
}
}
}
}
(...)
xAxis: {
tickPositions: [-0.5, 6.5, 7.5],
showLastLabel: false,
labels: {
formatter: function() {
switch (this.pos) {
case -0.5:
return 'Bananas';
case 6.5:
return 'Apples';
}
}
}
}
Live demo: http://jsfiddle.net/BlackLabel/2Lcs5up5/
I need to stack the bars in the bar chart to the left as per the image attached
is there a way to do that in chart.js?
EDIT:
Just to clarify what I am looking for.
The number of the bars in my chart is dynamic, if there are 10 of them then chart looks fine but if there are only 2 they each take 50% of the width of the chart (see picture #2)
I want both of those bars to be exactly the same width as if there were 10 of them and be stacked to the left.
One option that I'm currently considering is just to add (10 - no of bars) bars with 0 value so that they won't be visible. But I'm hoping that there is a better solution.
Thanks.
Instead of creating a graph with 10 empty bar charts, then populate it with your values, I think it would be better to add empty values to reach the number of 10 (same idea though).
If you take a look in the Chart.js documentation, you can see that you can create plugins for your charts and graphs. Plugins are extremely useful when editing your chart (instead of just hardcoding what you want) since they allow you to handle what is happening while creating your charts.
For instance : beforeUpdate, afterDraw are some of the events you can handle with plugins.
Now, you must know that the chart object contains a lot of information :
If you want to edit a global option, you'd check chart.config.options
If you want to edit a specific chart, you'd check chart.config.data
In our case, we'd need the data attribute.
If you take a deep look in it, you'd see that the number of values come from the lengh of both data.labels and data.datasets[n].data (n being the nth dataset).
Now that you know what to do, and how to do it, you can do it.
I still made a quick example of what you are looking for :
var ctx = document.getElementById("myChart").getContext("2d");
// stores the number of bars you have at the beginning.
var length = -1;
// creates a new plugin
Chart.pluginService.register({
// before the update ..
beforeUpdate: function(chart) {
var data = chart.config.data;
for (var i = data.labels.length; i < data.maxBarNumber; i++) {
length = (length == -1) ? i : length;
// populates both arrays with default values, you can put anything though
data.labels[i] = i;
data.datasets[0].data[i] = 0;
}
},
// after the update ..
afterUpdate: function(chart) {
console.log(chart);
var data = chart.config.data;
if (length == -1) return;
// prevents new charts to be drawn
for (var i = length; i < data.maxBarNumber; i++) {
data.datasets[0]._meta[0].data[i].draw = function() {
return
};
}
}
});
var data = {
// change here depending on how many bar charts you can have
maxBarNumber: 10,
labels: [1, 2, 3, 4, 5, 6, 7],
datasets: [{
label: "dataset",
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255,99,132,1)',
borderWidth: 1,
data: [65, 59, 80, 81, 56, 55, 40],
}]
};
var myBarChart = new Chart(ctx, {
type: 'bar',
data: data,
options: {
scales: {
xAxes: [{
display: false
}],
yAxes: [{
stacked: true
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.2.1/Chart.min.js"></script>
<canvas id="myChart" width="400" height="400"></canvas>
You can use Array.prototype.reverse() to reverse the data if it is currently stacked to the right. Otherwise you will need to use some type of sorting to go from largest data to smallest data.
I have a chart with a yAxis, that has a minimum of -5, and max of 5.
The yAxis has these labels: -5, -2.5, 0, 2.5, 5.
My config is so close - I have the right amount of grid/plot lines, but I want to hide a couple of the text labels in the yAxis (not the actual lines relating to the label).
In other words, I want to remove or hide the -2.5 and 2.5 labels.
I've tried various methods in the yAxis, eg step, but it's not achieving what I want.
yAxis: {
labels: {
step: 5
}
}
JSFiddle
Any ideas how to achieve this?
I nearly didn't post this question because I found a (non-SO) answer - perhaps this will help others.
I don't know if this is the most elegant approach for highcharts, but you can use the label formatter to achieve this.
In my case, instead of this:
labels: {
formatter: function () {
return this.value+'%';
}
}
We can add a conditional to check the label's value, and only return something if it's what we want. All together:
yAxis: {
//...
labels: {
formatter: function () {
if (this.value !== -2.5 && this.value !== 2.5) {
return this.value+'%';
}
},
step: 1
},
//...
},
Example
Warning: hard coding some values and depending on them in this way is risky if you have dynamic data. For this instance we don't have dynamic data, they will be fixed, so it's safe for us. Another approach could be to iterate over each value/label and only return every X child as you require.
I'm writing Ext JS 4 line chart component. It works all fine, but when I display labels under axis they are just too dense. I won't the number of visible labels to decrease. How to do that? Here's my code for the axis:
{
type: 'Category',
position: 'bottom',
fields: ['date'],
grid: true,
label: {
field: 'label',
rotate: { degrees:315 },
renderer: function(item) {
var date = new Date(item);
/* parseIntToStringWithZeros is a custom method somewhere else */
var day = parseIntToStringWithZeros(date.getDate());
var month = parseIntToStringWithZeros(date.getMonth());
result = day + '-' + month;
return result;
}
}
}
I think you might need to use a "Numeric" axis instead of a "Category" axis a start (not sure what your data looks like, but you might need to convert times to values to get it to work).
With the numeric axis you are supposed to be able to set the number of major tick values (where the grid lines and labels appear), by setting the steps property in the axis config; however this doesn't always work. A more surefire way is to override the applyData function which isn't documented so you need to search through the dev code to see what it's doing.
Also, to simply not render a particular label you can just return the empty string in the label renderer function. e.g. if you only want an individual "month" to show up once in the above code you could do something like this..
label: {
....
renderer: (function(){
var lastRenderedMonth = '';
return function(item){
... //your code above without the return..
if(month == lastRenderedMonth)
return "";
lastRenderedMonth = month;
return result;
};
})(),
...