I made a small fiddle based on the highcharts demos at http://jsfiddle.net/w53woene/1/
The issue I have boils down to four things:
1) I need to rotate the labels (this is based on a feature request so I can't ignore it).
labels: {
rotation: -45
}
2) The fact that the last label is really really long. This is based on data which is given for each chart so it's not always the case but it's the case around 30%-40% of the time so quite often.
3) Labels shouldn't be wrapped or shortened with an ellipsis.
4) I need to add spacing between the chart and the legend to adapt to the labels size but only when the label is going to be too long and colliding with the legend.
This only seems to happen when the chart: { inverted: true } and if it's not inverted the legend behaves normally, increasing the distance from the chart as needed.
Ideally I'd like to know if there's a specific option I'm missing that achieves this natively, i.e., I would have expected floating: false to achieve this the same way it does when the chart is not inverted, but it doesn't seem to. If this isn't possible I'd like to know how I can adjust the chart size (excluding the legend) in order to achieve this via JavaScript.
The problem is that vertical axis add space on the left size, but doesn't add on the bottom. I would wrap labels for a better readability: http://jsfiddle.net/w53woene/2/ - otherwise it may happen that you will have so long label that chart will get 0 pixels for plotting area.
If you really need non-wrapped text, then I would wrap Axis.prototype. getOffset to add extra space:
(function(H) {
H.wrap(H.Axis.prototype, 'getOffset', function(p) {
p.call(this);
if (this.isXAxis) {
var lastTick = this.tickPositions[this.tickPositions.length - 1],
lastLabel = this.ticks[lastTick].label,
height = lastLabel.getBBox(true).height;
this.labelDiff = height - this.chart.marginBottom;
if (this.labelDiff > this.chart.axisOffset[2]) {
this.chart.axisOffset[2] = this.labelDiff;
} else {
this.labelDiff = 0;
}
} else {
this.offset -= this.chart.xAxis[0].labelDiff;
}
});
})(Highcharts)
And live demo: http://jsfiddle.net/vwegeuvy/
Related
I've got a line-chart with potentially more than 10 points. It will be drawn inside a container element with fixed width (let's say 800px).
In case the points count gets more than 10, I need to make the chart scrollable in a way which initially displays only the last 10 points.
Here's the fiddle for what I have right now:
https://jsfiddle.net/kpx13oz9/69/
Currently, I have the scroll-bar initially sitting on the rightmost position (which is what I want). But, as I increase the number of totalItemCount, more points are included inside the scrollable plot and some of the ticks on the x-axis become hidden.
I'm looking for a configuration which enforces the following:
regardless of the number of points, display the latest 10 points. the rest of the points will be accessible by horizontal scrollbar.
All the ticks on the x-axis need to be displayed always. No auto-hide.
You can dynamically calculate the minWidth property based on the created chart. To show all of the labels, set xAxis.tickInterval to one day.
chart: {
events: {
load: function() {
const minWidth = this.plotSizeX / 9 * workOrderHistory.length;
this.update({
chart: {
scrollablePlotArea: {
scrollPositionX: 1,
minWidth
}
}
}, false);
this.xAxis[0].update({
width: '100%'
});
},
render: drawCrosshair(crosshair, 'red')
}
}
Live demo: https://jsfiddle.net/BlackLabel/v3zowk9e/
API Reference: https://api.highcharts.com/highcharts/xAxis
My Highcharts solutions give the user the option to control which series are showed at any one point. Because of the amount of series available, I am extending the functionality to checkboxes rather than just add them all on initiation and hide the majority initially as this would make the legend huge.
I would like to overlay a button on the chart to make it look integrated. This gives no problems in itself as I can give a negative value to legend.x to move it to make room for the button. However, this then poses a problem when more series are programmatically added, as the legend maintains its original width and I lose some options off to the side when there are too many.
This is a stripped down fiddle of my problem: https://jsfiddle.net/paLoxcy3/. This is a relevant snippet:
legend: {
align: "right",
x: -100
}
It's worth adding here the graph needs to maintain a responsive width, hence I cannot just add a width. As an aside, were I do to this (and indeed when the series names do eventually drop a line in my current fiddle), they then become left aligned which is not desired.
I've had a good play around with the options of legend but at this point assume the only solution to effectively add padding-right to the legend holder is to use chart.events.load and chart.events.redraw to somehow do it manually? It's a bit annoying as the options to add marginTop and
marginBottom exist but not marginLeft and marginRight.
Any help much appreciated! Shortcut to docs is here to save some time :)
I think that this problem is connected with small issue in Highcharts legend.renderItem function. As a workaround you can change if statement responsible for moving item to another line. Here you can see how this statement looks in code:
// if the item exceeds the width, start a new line
if (horizontal && legend.itemX - initialItemX + itemWidth >
(widthOption || (chart.chartWidth - 2 * padding - initialItemX - options.x))) {
legend.itemX = initialItemX;
legend.itemY += itemMarginTop + legend.lastLineHeight + itemMarginBottom;
legend.lastLineHeight = 0; // reset for next line (#915, #3976)
}
And Here you can see how I have changed this statement:
// if the item exceeds the width, start a new line
if (horizontal && legend.itemX - initialItemX + itemWidth >
(widthOption || (chart.chartWidth - 2 * padding - initialItemX - ((options.align === 'right') ? (-options.x) : options.x)))) {
legend.itemX = initialItemX;
legend.itemY += itemMarginTop + legend.lastLineHeight + itemMarginBottom;
legend.lastLineHeight = 0; // reset for next line (#915, #3976)
}
Here you can find Github topic connected with this issue:
https://github.com/highcharts/highcharts/issues/5443
And here you can see chart with my workaround:
https://jsfiddle.net/paLoxcy3/2/
Best regards.
I am working with highcharts and having some problems with stackLabels configuration of highcharts. I was asked to display a bar chart with "score/full score" format, e.g. 6 out of 10 should be like
⬛︎⬛︎⬛︎⬛︎⬛︎⬛︎⬜︎⬜︎⬜︎⬜︎ 6/10
And I faked it with stacked bar chart with 6 for the first part of the bar and 4 for the second part.
[Solved] However, I don't know how to display 6/10 in stackLabels (It seems that I can only use {total} in it, while I can use {point.y}/{point.fullscore} in dataLabels).
[Unsolved] And also, when I use the basic bar chart, dataLabels automatically adjust its position (In my case, dataLabels will show on the left of the right edge of the bar). However, how should I force the stackLabels to display?
This answer points out that there is no enough space for stackLabels to display, and the solution is to make max value bigger and leave some space for it. However, the solution is not that elegant to me, and also since my bar chart is horizontal and the label should be long.
Any help will be appreciated! Thanks in advance!
To answer second question:
You can set yAxis.stackLabels.crop to false, so labels will be rendered always. Those labels won't show up inside the plotting area, but will be rendered somewhere outside. stackLabels are not dataLabels - dataLabels have option justify, which forces labels to be rendered inside the plotting area.
However, in Highcharts you can get access to those labels, and move them (that's why crop needs to be set to false - to render labels anyway), here is simple POC:
function updateStacks() {
var chart = this,
H = Highcharts,
padding = 15,
item,
bbox;
for (var stackName in chart.yAxis[0].stacks) {
for (var itemName in chart.yAxis[0].stacks[stackName]) {
item = chart.yAxis[0].stacks[stackName][itemName]; // get stack item
bbox = item.label.getBBox(true); // get label's bounding box
// if label is outside, translate it:
if (bbox.width + bbox.x > chart.plotWidth) {
// add some poding, for a better look&feel:
item.label.translate(-bbox.width - padding);
}
}
}
}
Now simply use that method in load and redraw events, here you are:
chart: {
type: 'bar',
events: {
redraw: updateStacks,
load: updateStacks
}
},
And live demo: http://jsfiddle.net/awe4abwk/ - let me know if something is not clear.
PS: Your answer with formatter for stackLabels is good!
Use formatter to change default stack label. You can access axis label with this.axis.
stackLabels: {
enabled: true,
formatter: function() {
return (this.axis.series[1].yData[this.x] / this.total * 100).toPrecision(2) + '%';
//Make your changes here to reflect your output. This is just a sample code
}
}
Now to always show stack labels you have two options which are described here.
Link
Okay, I've found the solution for the first question: How to show the specific series values in stackLabels? from this answer.
In my case, the code should be:
yAxis: {
stackLabels: {
formatter: function() {
return [this.axis.series[1].yData[this.x], '/', this.total].join('');
},
enabled: true,
}
}
If you have better solution, post it! And I still have no idea about how to find it in the docs, maybe I should dig more...
I am going round in circles a bit with highcharts trying to make a chart as follows:
And here is a jsbin of the above example:
http://jsbin.com/pezufeweki/1/
My main issue is I have to use a fake segment (and make it white) for the values to be correct (which in turn means the 'hidden' section still has a tooltip.
Is there a way in highcharts to make a segment 'hidden' from the pie chart?
Alternatively, I can get away with making it white but then I would need to hide the tool tip just for that segment as I need it everywhere else.
An added bonus would be for the inner (red) area to start anti-clockwise.
Any pointers much appreciated.
Cheers.
Fixed hiding the tooltip per item
formatter: function() {
if (this.key === "hide") {
return false;
}
else return this.y;
}
{
name: 'hide',
y: 12,
color:'red'
},
There are plenty examples of hiding an Y-Axis without hiding the series - But i need the opposite of that.
I want to make a series invisible while still displaying the y-axis and i dont know how!
Why?
Because i have 2 diagrams which are perfectly aligned by their x-Axis:
But when i disable both Temperature fields, the axis will be lost, and the diagrams are incorrect in size:
I also don't like the idea, of disabling all and loosing whole diagram style:
There is no problem by having an empty diagram, but having a blank box isn't the style i want to reach.
I tried to manipulate the axis in within the legendItemClick event, but selecting the correct axis and set their value visible didn't work out. How can I solve this problem?
I think you have two options:
play around with margins - to make them fixed
set min and max for axis, so labels will remain on a chart: example
if you don't know min/max values at start you can change them within the legendItemClick as following:
plotOptions: {
series: {
events: {
legendItemClick: function() {
var temperatur = this.chart.series[0];
var taupunkt = this.chart.series[1];
var other = this.name=="Lufttemperatur"?taupunkt:temperatur;
var element = this;
if(this.name == "Lufttemperatur" || this.name == "Taupunkt") {
if(this.visible && !other.visible) {
//both will be invisible soon
this.chart.yAxis[0].update({
min:element.yAxis.min,
max:element.yAxis.max
});
} else {
//one will be visible
this.chart.yAxis[0].update({
min:null,
max:null
});
}
}
}
}
},
}
This will only set min/max if there is no one of your series visible, but deletes it if you have one visible