In my chart, I have a column with a value of -1. This is to indicate that the value for this series is not available.
In the chart, I would like to add a label rotated 90 degrees that says "No data" where the bar should be. Can someone provide me with an example for how to accomplish this?
If you have a column with -1 it will still be rendered. You need to map -1 values to null. After that you can use renderer.text function and render a rotated text for the null columns.
Function for rendering/redrawing labels:
function renderLabels() {
this.series[0].points.forEach((point, i) => {
if (point.isNull) {
point.noDataLabel = this.renderer.text('No data', 0, -9e9).attr({
rotation: 90,
zIndex: 99
}).add()
}
})
redrawLabels.call(this)
}
function redrawLabels() {
this.series[0].points.forEach(point => {
if (point.noDataLabel) {
point.noDataLabel.attr({
x: point.series.group.translateX + point.plotX,
y: this.plotTop + this.plotHeight - 80
})
}
})
}
Attach functions on load and redraw events, and map data:
Highcharts.chart('container', {
chart: {
type: 'column',
events: {
load: renderLabels,
redraw: redrawLabels
}
},
series: [{
data: data.map(v => v === -1 ? null : v)
}]
});
Example and output
http://jsfiddle.net/n3nuhvL7/
Related
How would I go about and make custom image and text for an axis tick in highcharts.js? I need a normal number as label for the tick and then a small colored square image (dependant on another variable from a special array of values) displayed below the number? This is what I need to do, see image below
You can create an additional HTML element for rectangles and in 'load' event use color-axis to translate values into background colors.
const colorValues = [5, 9, 14, 15, 1, 4];
Highcharts.chart('container', {
...,
chart: {
events: {
load: function() {
const rectangles = document.getElementsByClassName('label-rect'),
colorAxis = this.colorAxis[0];
[].forEach.call(rectangles, function(rect) {
rect.style.backgroundColor = colorAxis.toColor(rect.dataset.value);
});
}
}
},
xAxis: {
type: 'category',
labels: {
style: {
'text-align': 'center'
},
useHTML: true,
formatter: function() {
return this.pos + '<br/>' +
'<span data-value="' + colorValues[this.pos] +
'" class="label-rect"></span>'
}
}
}
});
Live demo: http://jsfiddle.net/BlackLabel/7146g2r3/
API Reference:
https://api.highcharts.com/highcharts/xAxis.labels
https://api.highcharts.com/highcharts/colorAxis
I would like to display only the first and the last label on my xAxis. This would give enough of a »frame« for the user. However, I don't succeed in it. I am working with a synchronized chart, which can be found here. Is the second and third »column« of (smaller) graphs, I am targeting at.
I tried to work with »startOnTick« and »endOnTick«, but it won't do it.
xAxis: {
crosshair: true,
events: {
setExtremes: syncExtremes
},
visible: i === 1,
showFirstlabel: true,
showLastlabel: true,
startOnTick: true,
endOnTick: true,
labels: {
step: 500,
format: '{value}'
}
},
What is the correct way to force Highcharts to display only first and last label?
Here is a short fiddle (don't know why the line does not appear; it shows the values with mouseover...).
Thanks for any hints.
You can use the xAxis.labels.formatter callback to show wanted ticks:
Demo: https://jsfiddle.net/BlackLabel/m2Ln8sdg/
xAxis: {
tickAmount: 10,
labels: {
formatter() {
if(this.isFirst || this.isLast) {
return this.value
} else {
return ''
}
}
}
},
API: https://api.highcharts.com/highcharts/xAxis.labels.formatter
If you want to have more control about it (like hide label and tick) you can use the load callback method and proper logic to hide/show ticks:
Demo: https://jsfiddle.net/BlackLabel/fbcdskmv/
chart: {
events: {
load() {
let chart = this;
for (let i in chart.xAxis[0].ticks) {
//hide all
chart.xAxis[0].ticks[i].label.hide()
chart.xAxis[0].ticks[i].mark.hide()
// show first and last tick
if (chart.xAxis[0].ticks[i].isFirst || chart.xAxis[0].ticks[i].isLast) {
chart.xAxis[0].ticks[i].mark.show()
chart.xAxis[0].ticks[i].label.show()
}
}
}
}
API: https://api.highcharts.com/highcharts/chart.events.load
Or use the tickPositioner callback to achieve it: https://api.highcharts.com/highcharts/xAxis.tickPositioner
The above answers are good, but i just wanted to show another approach which works just fine.
In my styling i do this;
.highcharts-xaxis-labels > text:not(:first-child):not(:last-child) {
visibility: hidden !important;
}
Summing up #luftikus143 and #Sebastian Wędzel comments:
const data = [
['Jan 2020', 167],
['Feb 2020', 170],
['Mar 2020', 172]
];
xAxis: {
type: 'category',
tickPositions: [0, data.length - 1]
}
Will output only the first and last labels. #martinethyl's workaround answer might need some extra tweaks specially if you have multiple data points. Suggestions (might not work well with smaller media types):
xAxis: {
type: 'category',
labels: {
rotation: 0,
x: 5, // Optional: moves labels along the x-axis
style: {
textOverflow: 'none', // Removes ellipsis
whiteSpace: 'nowrap', // Gets the label text in one line
},
},
Is there a way to update specific xAxis label when a user wants to print the chart?
This is the initial chart (inverted column with custom styles). I created xAxis labels with formatter function
xAxis: {
arrowOnEnd: true,
labels: {
x: 40,
formatter: function() {
if (this.isLast) { return 'Less Likely' }
if (this.isFirst) { return 'More Likely' }
}
},
and was able to position them so it looks like inverted xAxis has values from Less likely to More likely as follows
events: {
load: function() {
// https://stackoverflow.com/questions/51656849/xaxis-point-specific-label-positions
const chart = this;
Highcharts.objectEach(chart.xAxis[0].ticks, function(tick: any) {
if (!tick.isNewLabel && tick.isFirst) { tick.label.attr({ y: 25 })}
if (!tick.isNewLabel && tick.isLast) { tick.label.attr({ y: 590 })}
});
},
The problem I have is when I want to print the chart:
xAxis labels gets back to its original location.
I tried to replicate events: { load: ... } logic in beforePrint or wrapping 'print' event to highcharts object, but that doesn't seem to take any effect
beforePrint: function() {
const chart = this;
Highcharts.objectEach(chart.xAxis[0].ticks, function(tick: any) {
if (tick.isFirst) { tick.label.attr({ y: 125 })}
if (tick.isLast) { tick.label.attr({ y: 590 })}
});
}
Highcharts.wrap(Highcharts.Chart.prototype, 'print', function (proceed) {
const chart = this;
Highcharts.objectEach(chart.xAxis[0].ticks, function(tick: any) {
if (tick.isFirst) { tick.label.attr({ y: 25 })}
if (tick.isLast) { tick.label.attr({ y: 590 })}
});
proceed.call(this)
});
I tried tickPositionier but couldn't actually work out how to make that work.
Worth to mention. that afterPrint event with the same logic resets the chart, so I don't understand why one event is working (afterPrint) but the other doesn't (print)
afterPrint: function() {
const chart = this;
Highcharts.objectEach(chart.xAxis[0].ticks, function(tick: any) {
if (tick.isFirst) { tick.label.attr({ y: 25 })}
if (tick.isLast) { tick.label.attr({ y: 590 })}
});
},
Am I missing something here? Why I can't update tick position for printing?
Sidenote - jsfiddle gives me 502 Bad Gateway so I'm unable to demo it there
Changing the load event to render should fix the issue when the chart will be initialized again before the print.
Demo: https://jsfiddle.net/BlackLabel/4qbp1w0z/
chart: {
type: 'bar',
events: {
render: function() {
// https://stackoverflow.com/questions/51656849/xaxis-point-specific-label-positions
const chart = this;
Highcharts.objectEach(chart.xAxis[0].ticks, function(tick) {
if (!tick.isNewLabel && tick.isFirst) {
tick.label.attr({
y: 20
})
}
if (!tick.isNewLabel && tick.isLast) {
tick.label.attr({
y: 350
})
}
});
},
}
},
API: https://api.highcharts.com/highcharts/chart.events.render
How can I hide a stackLabel (a label above the column) in highCharts when the text in the label is bigger than the column (As you can see in the image below).
I want to show empty string when my data exceeds the column width.
This is part of the code which is responsible for showing the data:
yAxis: {
stackLabels: {
style: {
color: 'black'
},
enabled: true
formatter: function () {
return this.total + " mm ";
}
}
}
Create a function which loop through the stack labels and compare their width with the point's width - according to the comparison hide or show the label.
Function can look like this:
function hideLabel() {
const axis = this.yAxis[0];
const stacks = axis.stacks.column;
const pointWidth = this.series[0].points[0].shapeArgs.width;
Object.keys(stacks).forEach(key => {
const label = stacks[key].label;
label.attr({
visibility: label.getBBox().width > pointWidth ? 'hidden' : 'visible'
});
})
}
Set this function on load and redraw events:
chart: {
type: 'column',
events: {
load: hideLabel,
redraw: hideLabel
}
},
example: http://jsfiddle.net/nq5ke47z/
I have a stacked group column graph as shown in the fiddle provided below. In the xAxis labels (red blocks), I would like to display the total of the stacked amounts subtracted from the total of the second column. For example, for "Value1" I want to display 42 in the label (100-(43+15)). Right now, I am only able to access x values, which I am returning in the formatter function (this.value). https://jsfiddle.net/er1187/n6sr0znx/
xAxis: [{
offset: -280,
tickWidth: 0,
lineWidth: 0,
categories: ['Value1', 'Value2', 'Value3'],
labels: {
x: 5,
useHTML: true,
style:{
backgroundColor: 'red',
color: 'white'
},
formatter: function () {
return this.value;
}
}
}, {
linkedTo: 0,
categories: ['Value1', 'Value2', 'Value3']
}]
In the axis formatter you dont have access to the processed data yet. However, you can access the options of series and obtain the raw data.
formatter: function () {
const axis = this.axis;
const points = axis.series.map(series =>
series.options.data[axis.categories.indexOf(this.value)]
);
return points[2] - (points[0] + points[1]);
}
example: https://jsfiddle.net/n6sr0znx/2/