highcharts: 8.0.4
I have created a jsfiddle to demonstrate what is happening, showing a pie and bar chart and how they behave differently. In my scenario the chart is instantiated without data. Then data is requested from an api (simulate this in the jsfiddle by clicking the buttons). The charts display, then data is refreshed (click the buttons again). You should notice that the pie chart "disappears" while the bar chart does not. I would guess this is because the "color" property is set to null.
My question is why does the pie chart behave differently to the bar chart?
In my scenario I may or may not have a "color" value returned from the api. I did come up with a solution to conditionally add the property only if it has a value:
data.map((d) => {
if (d.color) {
return (
{
name: d.group,
y: d.value,
color: d.color,
id: d.id,
});
}
return (
{
name: d.group,
y: d.value,
id: d.id,
});
});
This does seem to work, however (here is my second question), is there a better way to do this?
For this particular use case:
You can use the update instead of the setData feature.
Demo: https://jsfiddle.net/BlackLabel/1bjfh63c/
Code:
button1.addEventListener('click', function() {
let x = Math.floor(Math.random() * (99)) + 1;
let y = Math.floor(Math.random() * (99)) + 1;
chart1.series[0].setData(
[{
y: x,
name: 'Apple',
color: null,
id: 1,
},
{
y: y,
name: 'Peach',
color: null,
id: 2,
}
], false, false, false);
chart1.redraw();
})
In setData set the updatePoints flag to false - that's why the error occurred.
demo: https://jsfiddle.net/BlackLabel/umo14be0/
API: https://api.highcharts.com/class-reference/Highcharts.Series#setData
Related
I have a simple Highchart with a dataset of up to 1000 datas. There are only y values the x values are generated automatically. Also, the values come from my nodejs server so please don't be surprised about the notation.
Now I want 3 special values whose x and y values are known to be highlighted. In which way doesn't matter for now.
One possibility would be to show the point at the location, otherwise they are not displayed. The problem I have is that I don't know how to control a specific point.
var chart1 = new Highcharts.Chart({
chart: {
renderTo: 'chart-emg1',
type: 'line'
},
title: {
text: 'EMG 1'
},
xAxis: {
tickInterval: 1
},
yAxis: {
title: { text: 'Voltage'}
},
series: [{
data: [<%-data1 %>]
}]
});
You can use the load event and update specific points. For example:
events: {
load: function() {
this.series[0].points.forEach(point => {
const isPointToHighlight = pointsToHighlight.some(
p => p.x === point.x && p.y === point.y
);
if (isPointToHighlight) {
point.update({
color: 'red',
marker: {
enabled: true
}
}, false);
}
});
this.redraw();
}
}
Live demo: http://jsfiddle.net/BlackLabel/tLd3j78f/
API Reference:
https://api.highcharts.com/highcharts/chart.events.load
https://api.highcharts.com/class-reference/Highcharts.Point#update
I'm trying to create a dynamic flag series to go onto my main chart which is an OHLC series. I want to be able to freely drag each flag and when released, it will snap to the closest y value (either 'High' or 'Low') of the current OHLC bar.
I have been able to acheive this behavior with the following code:
Highcharts.getJSON('https://www.highcharts.com/samples/data/aapl-ohlc.json', function (data) {
var lastDate = data[data.length - 1][0], // Get year of last data point
days = 24 * 36e5; // Milliseconds in a day
// create the chart
Highcharts.stockChart('container', {
tooltip: {
enabled: false
},
rangeSelector: {
selected: 0
},
xAxis :{
crosshair: {
width: 0
}
},
title: {
text: 'AAPL Stock Price'
},
series: [{
type: 'ohlc',
name: 'AAPL Stock Price',
tooltip: {
visible: false
},
data: data,
dataGrouping: {
units: [[
'week', // unit name
[1] // allowed multiples
], [
'month',
[1, 2, 3, 4, 6]
]]
}
},
{
type: 'flags',
align: 'right',
point: {
events: {
drop: snapFlag
}
},
dragDrop: {
draggableX: false,
draggableY: true,
},
data:[{
x:data[data.length - 20][0],
y: data[data.length - 20][2],
title: "A",
text: "1: test",
snapto:'closest',
title: 'A',
}]
}]
});
});
function snapFlag(){
/*
* on drop event, find the nearest OHLC point and snap flag to that point
* this will be the default unless "snapto" property is set to "none"
*/
const old_x = this.x;
const old_y = this.y;
let new_graphic = this.graphic;
let closest_y;
let ohlc_point;
// iterate x values in reverse to find matching ohlc point
ohlc_point = this.series.chart.series[0].points.reverse().filter(function( point){
return point.x === old_x;
})[0];
if(this.snapto === "closest"){
// find the OHLC point that is closest to drop location
closest_y = [ohlc_point.high,ohlc_point.low].reduce(function(prev, curr) {
return (Math.abs(curr - old_y) < Math.abs(prev - old_y) ? curr : prev);
});
}else if(this.snapto !== "float"){
closest_y = ohlc_point[this.snapto]
}else{
closest_y = this.y
}
if(closest_y === ohlc_point.low){
this.graphic.box.element.setAttribute('transform', `translate(0,${this.graphic.box.getBBox().height}) scale(1,-1) translate(0,-${this.graphic.box.getBBox().height})`);
}else{
this.graphic.box.element.removeAttribute('transform');
}
this.update({
y: closest_y
},true,false);
// override the default behavior
return false;
}
<script src="https://code.highcharts.com/stock/highstock.js"></script>
<script src="https://code.highcharts.com/stock/modules/data.js"></script>
<script src="https://code.highcharts.com/stock/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/draggable-points.js"></script>
<div id="container" style="height: 400px; min-width: 310px"></div>
And the JSFiddle here
What I'm having trouble with is the SVG transformation. As you can see I have successfully been able to mirror the flag's SVG path about the x-axis such that it does not overlap with the OHLC bar that its attached to. But I am having trouble getting the flag title to adhere to the same translation. I have tried similar methods of directly editing the element that hold the title but have not been able to get the changes to stick.
Any help on this or a different direction I could take to accomplish this would be appreciated. I have been stuck on this small detail for a while!
I think that you should be able to achieve it by translating the graphic.tex separately.
Part of the logic which has been changed:
if (closest_y === ohlc_point.low) {
this.graphic.box.element.setAttribute('transform', `translate(0,${this.graphic.box.getBBox().height}) scale(1,-1) translate(0,-${this.graphic.box.getBBox().height})`);
this.graphic.text.translate(0, this.graphic.box.getBBox().height + this.graphic.text.getBBox().height)
} else {
this.graphic.box.element.removeAttribute('transform');
this.graphic.text.element.removeAttribute('transform');
}
Demo: https://jsfiddle.net/BlackLabel/vy1gzwmd/
I have created variable pie chart using highchart. In legend section I need to show one name and one value. so I put value in span tag. Now I can't align the value. The external styles are not working. Once we inspect that span tag, automatically generate dx="0". When we edit that value in console it's working. But when we give it to internal style or external style, it's not working.
I need alternate style attribute of "dx". The above image dx value is automatically generated.
data: [
{
name: 'Name Score '+'<span class="score-percentage" >99% </span>',
y: 100,
z: 99
}
]
This is what I did in code. Using span class "score-percentage" to fill color black.
To have more flexibility, you can use Highcharts.SVGRenderer to add the values, for example in this way:
chart: {
type: 'variablepie',
events: {
render: function() {
var legend = this.legend,
legendItems = this.legend.allItems;
legendItems.forEach(function(item, i) {
if (!legend.customLabels) {
this.renderer.text(
legendValues[i],
110,
item.itemHeight
).attr({
}).add(item.legendGroup)
}
item.legendItem.css({
color: item.color
})
}, this);
legend.customLabels = true;
}
}
},
legend: {
align: 'left',
squareSymbol: false,
padding: 0,
symbolWidth: 0,
layout: 'vertical'
},
Live demo: http://jsfiddle.net/BlackLabel/nsdv659x/
API: https://api.highcharts.com/highcharts/chart.events.render
Currently I have a request to have a Bullet Chart with two targets (min and max).
To do it I am simply using a normal Bullet Chart with a Scatter series to draw the other target. I would like to wrap this behavior inside the bullet chart, so it would have something like the following options:
series: [{
data: [{
y: 275,
target: 250,
minTarget: 100
}]
},
And then, on the wrap, I would get this minTarget and make a scatter plot automatically. How can I do it?
Here's the fiddle I have so far: http://jsfiddle.net/gwkxd02p/
I do not think that render is a good method to add another series - anyway, you can try to do it like this:
Highcharts.wrap(Highcharts.seriesTypes.bullet.prototype, 'render', function(p) {
if (!this.hasRendered) {
const scatterData = this.points
.map(({ x, y, options }) => ({
x,
y: options.minTarget !== undefined ? options.minTarget : null
}))
if (scatterData.length) {
const scatter = this.chart.addSeries({
type: 'scatter',
data: scatterData,
marker: {
symbol: 'line',
lineWidth: 3,
radius: 8,
lineColor: '#000'
}
}, false)
scatter.translate()
scatter.render()
}
}
p.call(this)
})
And data for bullet:
series: [{
data: [{
y: 275,
target: 250,
minTarget: 100
}, {
y: 100,
target: 50
}, {
y: 500,
target: 600,
minTarget: 20
}]
live example: http://jsfiddle.net/n4p0ezzw/
I think that the better place is bullet's init method but in that method the points do not exist yet - so you would have to match the x values (if it is needed) on your own.
My suggestion is - do not wrap Highcharts if you don't have to. A better (simpler, safer, cleaner, easier to debug, it does not change Highcharts internal code) practice would be to wrap the Highcharts constructor in a function and parse the options inside it and then call the chart constructor with new options, like this:
function customBullet(container, options) {
const newOptions = {} // parse options, check for minTarget, etc. and create new options
return Highcharts.chart(container, newOptions)
}
Currently, on enabling the legend of sunburst chart in Highcharts, single series label is seen in legend. Refer following JSFiddle: http://jsfiddle.net/amrutaJgtp/7r8eb5ms/6/
Highcharts pie chart legend shows the all the category labels in the legend. Refer following Pie chart legend: http://jsfiddle.net/amrutaJgtp/wgaak302/
series: [{
name: 'Sales',
colorByPoint: true,
showInLegend: true,
data: [{
name: 'Consumer',
y: 2455
}, {
name: 'Corporate',
y: 6802
},{
name: 'Home office',
y: 9031
}, {
name: 'Small Business',
y: 5551
}]
}]
Is it possible to show all the data points of the sunburst series or atleast the categories - Consumer, Corporate, Home Office, Small Business in legend?
In my opinion the answer is no.
Please refer to this demo: http://jsfiddle.net/kkulig/u3p1usz9/
I tried to implement this functionality by setting legendType = 'point' (not docummented in the API, but it works) and overwriting H.Legend.prototype.getAllItems, but it seems that hiding points is not supported for sunburst. There's no method that will do it - check out the console.log output. Switching visibility of the point by using visible property doesn't work either. Legend behaves properly, but there's no effect on the plot area.
Workaround
This simple example shows how to mimic desired legend behavior: http://jsfiddle.net/kkulig/kn10kb7L/
First I added additional two series that have no data:
{
type: 'line',
name: 'p1',
color: 'blue'
}, {
type: 'line',
name: 'p2',
color: 'blue'
}
The color of the legend item markers needs to be handled manually by setting the color of the 'fake' series.
I created visible flag for every leaf for controlling its visibility.
Then I used their legendItemClick callback function to filter the full data set and perform setData on the first series using the filtered data.
legendItemClick: function(e) {
series = this;
data.forEach(function(leaf) {
if (leaf.id === series.name || leaf.parent === series.name) {
leaf.visible = !leaf.visible;
}
});
this.chart.series[0].setData(data.filter((leaf) => leaf.visible));
}
API reference: https://api.highcharts.com/highcharts/plotOptions.sunburst.point.events.legendItemClick
If you think that hiding points should be implemented in sunburst series you can share this idea here: https://highcharts.uservoice.com/forums/55896-highcharts-javascript-api
Update
If you want an animation to happen use addPoint and removePoint instead of setData.
API references:
https://api.highcharts.com/class-reference/Highcharts.Series#addPoint
https://api.highcharts.com/class-reference/Highcharts.Series#removePoint