Dear Highcharts experts,
I am very new to highchart and slowly making my way up. I want to create a highchart combined with normal bar graph & with stacked bars. I am having issues in making it.
Below is my jsfiddle version
http://jsfiddle.net/wt45e6mL/9/
plotOptions: {
series: {
cursor: 'pointer',
pointPadding: 0,
groupPadding: 0.01,
point: {
events: {
click: function () {
if(this.category=='60') return false;
alert('Category: ' + this.category + ', value: ' + this.y);
selected = this;
},
}
},
allowPointSelect: true,
states: {
select: {
color: null,
borderWidth: 10,
borderColor: 'Blue'
}
}
},
column: {
stacking: 'normal',
dataLabels: {
enabled: false,
color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'white',
style: {
textShadow: '0 0 3px black'
}
}
},
}
Below are my difficult points:
1) Here for 60, 61, 62 I want to feed different data(series) to create a normal bar and rest stack bar. If the data is not there then do not create it. So stack bar should start from a value coming from another source , before that vale normal bars
2) I want to have a onclick event which i am able to do but this event is called on series data. When on click i want to highlight the whole stack bar along with the x axis value. The first three it should not be selected.
3) I want to feed one more data(series) which will show a straight line and highlight its y axis value.
Need some help on above points.
Thanks.
You've got a ridiculous amount of questions in this one question. In the future separate them out, it makes it easier to answer and it makes the questions more useful for future readers. But, let me see if I can help:
1) You can either use null for the series you don't want to overlap or use x and y coordinates
series: [{
name: 'A',
//stack: 1,
data: [{x:60, y:4000}, {x:61, y:4000}, {x:62, y:4000}, {x:63, y:4000}]
}, {
name: 'B',
id: 'B',
color: 'red',
//stack: 2,
data: [{x:63, y:1000}, {x:64, y:1000}, {x:65, y:1000}, {x:66, y:1000}, {x:67, y:1000}, {x:68, y:1000}, {x:69, y:1000}, {x:70, y:1000}]
}, {
name: 'C',
linkedTo:'B',
color: 'red',
//stack: 2,
data: [{x:66, y:400}, {x:67, y:400}, {x:68, y:400}, {x:69, y:400}, {x:70, y:400}, {x:71, y:400}, {x:72, y:400}, {x:73, y:400}, {x:74, y:400}]
},{
name: 'D',
//stack: 2,
data: [{x:63, y:400}, {x:64, y:600}, {x:65, y:700}, {x:66, y:800}, {x:67, y:1100}, {x:68, y:1100}, {x:69, y:1100}, {x:70, y:1100}, {x:71, y:1100}, {x:72, y:1100}, {x:73, y:1100}, {x:74, y:1100}]
}, {
name: 'E',
//stack: 2,
data: [{x:64, y:400}, {x:65, y:400},{x:66, y:400},{x:67, y:400}, {x:68, y:400}, {x:69, y:400}, {x:70, y:400}, {x:71, y:400}, {x:72, y:400}, {x:73, y:400}, {x:74, y:400}]
}]
2) You seem to have figured out the click events. Note that you can override the click event per series, so for your series that you don't want to have events just add a click event to the series that does nothing:
series: [{
name: 'A',
events: {
click: function() {
return true;
}
},
3) The line you are looking for is a y-Axis plot line
yAxis: {
labels: {
formatter: function() {
return '\u20ac' + ' ' + this.value
},
x: -20,
y: 0
},
plotLines: [{
value: 1000,
color: 'red',
width: 3,
zIndex: 10,
id: 'plot-line-1'
}],
4 - from comments) Showing 2 series as one color and one legend item for both. You can set each series' color manually. Use that to set them both as the same color. To only show one in the legend, use linkedTo
, {
name: 'B',
id: 'B',
color: 'red',
//stack: 2,
data: [{x:63, y:1000}, {x:64, y:1000}, {x:65, y:1000}, {x:66, y:1000}, {x:67, y:1000}, {x:68, y:1000}, {x:69, y:1000}, {x:70, y:1000}]
}, {
name: 'C',
linkedTo:'B',
color: 'red',
//stack: 2,
data: [{x:66, y:400}, {x:67, y:400}, {x:68, y:400}, {x:69, y:400}, {x:70, y:400}, {x:71, y:400}, {x:72, y:400}, {x:73, y:400}, {x:74, y:400}]
}
5 - also from comments) Highlighting the value on the axis. You can use the formatter function to modify the labels on the axis
yAxis: {
labels: {
useHTML: true,
formatter: function() {
if (this.value == 1000) return '<span class="highlight">' + '\u20ac' + ' ' + this.value + '</span>'
return '\u20ac' + ' ' + this.value
},
x: -20,
y: 0
},
http://jsfiddle.net/wt45e6mL/31/
1) This is complicated problem without simple solution. You can set grouping parameter to false for your stacked columns:
bar.grouping
You can use series.stack if you want to have three separate stacks for your columns instead of having one stack for columns:
stack
You can use groupPadding and pointPadding to 0 and your series.grouping to false. It should help you with your chart.
groupPadding
pointPadding
grouping
Here you can see example how it can work:
http://jsfiddle.net/y6tLaz1a/3/
2) Inside your click event callback function you can check if point category is smaller than 63. If It is smaller, you can return false like you are doing it right now. In other case you can iterate over all points and check if they have category equal to category of point you have just clicked. If they have the same category, you can select them using Point.select. Here you can read about this method:
Point.select
For example your click event callback function can look like that:
click: function() {
var point = this;
if (point.category <= 63) {
return false;
}
point.select(true, false)
Highcharts.each(this.series.chart.series, function(series, ind) {
Highcharts.each(series.data, function(p, i) {
if (p.category == point.category) {
p.select(true, true)
}
})
})
},
Here you can see example of this functionallity:
http://jsfiddle.net/wt45e6mL/22/
3) You can use plotLine in your case:
addPlotLine
You can add text to your plotLine by using label.text parameter:
label.text
Here you can see an example:
http://jsfiddle.net/eyLz09xv/
So taking all three examples into one, here you can see the final result:
http://jsfiddle.net/y6tLaz1a/4/
Related topic on highcharts forum:
http://forum.highcharts.com/highcharts-usage/bar-chart-with-stacked-bar-t34981/
Kind regards.
Related
I have a page with a variety of select menus. The select options are used in an ajax call to build a Highcharts bar graph. Every time a filter changes, the graph gets recreated. I did this instead of updating the series data, because in the past I have noticed that destroying and recreating was more efficient than updating.
I want images to show on the x-axis, so I used a nice little trick of creating two x axes, used formatter to return an image on the first axis, and linked the second axis to the first. This works on first refresh. However, every time the chart gets recreated thereafter, the image disappears. I checked my console and I don't see any errors.
And idea of what's going on here?
/**
* Whenselection changes
*/
$(document).on('change', '.filter', function(){
getChartData($params)
})
});
/**
* API call to get data that will populate charts.
* #param {obj} params
*/
function getChartData(params)
{
//Get chart data
$.ajax({
url: apiURL + '/chartdata/',
data: params,
dataType: 'json',
cache: false,
success: function(data) {
initChart(data[0]);
}
});
function initChart(chartData)
{
var chart = Highcharts.chart('container', {
chart: {
type: 'bar',
backgroundColor: 'transparent', //#E8EAF6',
height: '23%',
marginLeft: 35
},
title: {
text: null
},
xAxis: {
categories: [category1, category2],
lineColor: 'transparent',
min: 0,
tickColor: 'transparent',
title: {
text: null
},
labels: {
x: -35,
useHTML: true,
overflow: 'allow',
formatter: function () {
if(this.isFirst == true)
return '<img src="../assets/img/nat-jr-grad-gold.png"><br/>';
else
return '<img src="../assets/img/nat-jr-grad-purple.png"><br/>';
}
}
},
yAxis: {
min: 0,
title: {
useHTML: true,
text: null
},
labels: {
enabled: false
},
lineWidth: 0,
minorGridLineWidth: 0,
gridLineWidth: 0,
lineColor: 'transparent',
gridLineColor: 'transparent',
},
legend: {
enabled: false
},
series: [{
name: category1,
data: [{name: category1, y:Math.round(chartData.p_grad_nongap * 100), {y: null}],
}, {
name: category2,
data: [null, {name: category2, y: Math.round(chartData.p_grad_gap * 100)}]
}]
});
}
I reproduced your problem on a simplified example: http://jsfiddle.net/BlackLabel/sm2r684n/
For the first time, the image is loaded asynchronously and the chart does not take it into account when calculating the margins. Every next time the result is different, so you should wait until the picture is loaded:
var img = new Image();
img.onload = function() {
initChart();
}
img.src = "https://www.highcharts.com/samples/graphics/sun.png";
Live demo: http://jsfiddle.net/BlackLabel/m09ok2cg/
I think ppotaczek had the cortrect root cause of the problem; the image is loaded asynchronously and the chart does not take it into account when calculating the margins. His suggestion used setTimeout function to continuously redraw the graph, which is rather inefficient. My work-around for this was to just add the images as avg elements using chart.renderer after the chart was created.
/* Render X-Axis images */
chart.renderer.image('../assets/img/img1.png', 0, 40, 32, 36)
.css({
class: 'icon-img',
zIndex: 10
})
.add();
chart.renderer.image('../assets/img/img2.png', 0, 130, 32, 36)
.css({
class: 'icon-img',
zIndex: 10
})
.add();
I have implemented a plot line that's showing an average of all values:
"yAxis": {
plotLines: [{
color: 'red',
value: 22,
width: '1',
label: {
text: 'Average: 22'
}
}]
},
I want to change the average-value on legendItemClick. Is it possible to change some plotLine-properties programatically?
What I tried so far is this:
plotOptions: {
series: {
events: {
legendItemClick: function (event) {
var average = 15;
event.target.chart.userOptions.yAxis.plotLines[0].value = average;
event.target.chart.userOptions.yAxis.plotLines[0].label.text = 'Average: ' + average;
}
},
}
},
The value and the label-text aren't changing in the chart. Also I tried to redraw the chart but it still isn't changing the plot line. Is there a way to accomplish this?
I now have found an acceptable answer to my own question:
var average = 15;
event.target.chart.yAxis[0].removePlotLine('average');
event.target.chart.yAxis[0].addPlotLine({
id: 'average',
color: 'red',
width: 1,
value: average,
zIndex: 200,
label: {
text: 'Average: ' + average
}
});
I have to specify an ID for the plotLine, destroy it and create a new one.
My requirement is to create a spiderweb highchart with values on each axis where each axes have different scales.Is it possible? I have attached a same output of my requirement.
Use multiple yAxis and assign them to the separated panes with startAngle.
Parser
var colors = Highcharts.getOptions().colors,
each = Highcharts.each,
series = [{
yAxis: 0,
data: [10, 20, 30]
}, {
yAxis: 1,
data: [1, 2, 3]
}, {
yAxis: 2,
data: [4, 2, 1]
}, {
yAxis: 3,
data: [5, 1, 3]
}, {
yAxis: 4,
data: [2, 3, 4]
}],
yAxis = [],
panes = [],
startAngle = 0;
each(series, function(serie, i) {
yAxis.push({
pane: i,
showLastLabel: true,
gridLineWidth: i === 0 ? true : false,
labels: {
useHTML: true,
formatter: function() {
return '<span style="color:' + colors[i] + '">' + this.value + '</span>';
}
}
});
panes.push({
startAngle: startAngle
});
startAngle += 72;
});
Chart configuartion
$('#container').highcharts({
/*
chart options
*/
pane: panes,
yAxis: yAxis,
series: series
});
Example:
http://jsfiddle.net/6jmqb1r8/
I believe Sebastian Bochan's answer is rock solid due to his suggestion of the pane attributes. However, I was tinkering around with another method and wanted to share it with you and the community.
My solution makes use of "dummy" series, which are series that the user does not see or interact with, but can help with customized features such as your labels.
I added six "dummy" series that contain the labels for each spoke of the spider chart. The first, for the "zero" value, is blank, but the others will show data labels for the first, second, third, etc. points along the spoke.
After the chart is drawn, I use the addSeries() function to add these "dummy" series to the chart:
// Add "dummy series to control the custom labels.
// We will add one for each spoke, but first will always be
// overriden by y-axis labels; I could not figure out how to
// disable that behavior.
// The color, showInLegend, and enableMouseTracking attributes
// prevent the user from seeing or interacting with the series
// as they are only used for the custom labels.
var chart = $('#container').highcharts();
var labelArray = [
['','1','2','3','4','5'],
['','j','k','l','m','n'],
['','one','two','three','four','five'],
['','u','v','w','x','y'],
['','a','b','c','d','e']
];
for (var i = 0; i<=5; i++) {
chart.addSeries({
name: 'dummy series #' + i + ' for label placement',
data: [
{ name: labelArray[0][i], y: i },
{ name: labelArray[1][i], y: i },
{ name: labelArray[2][i], y: i },
{ name: labelArray[3][i], y: i },
{ name: labelArray[4][i], y: i }
],
dataLabels: {
enabled: true, padding: 0, y: 0,
formatter: function() {
return '<span style="font-weight: normal;">' + this.point.name + '</span>';
}
},
pointPlacement: 'on',
lineWidth: 0,
color: 'transparent',
showInLegend: false,
enableMouseTracking: false
});
}
A few items to note:
lineWidth: 0 and color: 'transparent' makes the "dummy" series lines invisible
showInLegend: false prevents them from showing up in the legend
enableMouseTracking: false prevents the users from interacting with them
Here is the fiddle that shows how this works: http://jsfiddle.net/brightmatrix/944d2p6q/
The result looks like this:
Just on quirk that I noted in my comments: I could not figure out how to override the labels on the first spoke (at the 12-o-clock position). If I set the y-axis labels to "false," it refused to show anything, even the custom data labels I set in the "dummy" series. Therefore, I suggest that your first spoke be the numerical labels in your example.
I realize this is perhaps a more complicated route, but I hope it's helpful to you and others in some way.
I'm using a chart with two series as you can see in http://jsfiddle.net/Charissima/zo5j94qz/4/
Is it possible to disable crosshairs for the second series? The problem is, that when the moise pointer is near the black series s2, there is no crosshair for the blue series s1 and I don't need/want crosshairs for the black series but for the blue.
var myData = [];
for (var i = 2; i < 10; i++) {
myData.push([i, i + Math.random() * 3]);
}
var myDataLine = [];
myDataLine.push([0,6]);
myDataLine.push([23,6]);
chart = $('#container').highcharts('StockChart', {
chart : {
zoomType: 'x',
},
series: [{
name: 's1',
data: myData,
type: 'line'
},{
name: 's2',
data: myDataLine,
type: 'line'
}]
});
Defaulty it is not built-in, but you can prepare your own crosshair function like in the example:
mouseOver: function () {
var chart = this.series.chart,
r = chart.renderer,
left = chart.plotLeft,
top = chart.plotTop,
width = chart.plotWidth,
height = chart.plotHeight,
x = this.plotX,
y = this.plotY;
if (this.series.options.enabledCrosshairs) {
crosshair = r.path(['M', left, top + y, 'L', left + width, top + y, 'M', left + x, top, 'L', left + x, top + height])
.attr({
'stroke-width': 1,
stroke: 'red'
})
.add();
}
},
mouseOut: function () {
if (crosshair.d !== UNDEFINED) crosshair.destroy();
}
http://jsfiddle.net/u4ha3cxw/7/
Yes, this is very possible. Use 2 x-axes, one with crosshair's enabled and the other with crosshairs disabled. Then specify which series you would like to use which axis:
Highcharts.chart('container', {
xAxis: [{
crosshair: {
enabled: true,
width: 5
}
}, {
crosshair: false,
visible: false
}],
series: [{
name: 'Series w/ Crosshair',
xAxis: 0, // You could omit this, highcharts uses the first axis in array by default
data: [99.9, 81.5, 76.4, 129.2, 144.0]
},
{
name: 'Series w/o Crosshair',
xAxis: 1,
data: [42.1, 47.5, 150.4, 42.2, 64.0]
}
]
})
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="container" style="height: 250px"></div>
Is there any way to make a Dual Axis in Highstock like this one on Highcharts?
http://www.highcharts.com/demo/combo-dual-axes
It looks like you can do it the same way as in highcharts. Each series specifies yAxis:n where n is the axis you want to plot it against, and you need to define n yAxis entries. I modified one of the example highstock demos: http://jsfiddle.net/ykfmG/
$.getJSON('http://www.highcharts.com/samples/data/jsonp.php?filename='+ name.toLowerCase() +'-c.json&callback=?', function(data) {
seriesOptions[i] = {
name: name,
data: data,
yAxis:i
};
}
yAxis: [{
labels: {
formatter: function() {
return (this.value > 0 ? '+' : '') + this.value + '%';
}
},
plotLines: [{
value: 0,
width: 2,
color: 'silver'
}]
},{opposite:true},{opposite:true},{opposite:true}],