When I clicked in legend, the plotBands parameters hide() and show() do not work.
I wish I could click on the legend and remove a specific PlotBand.
I tried using destroy() but it cleans the html dom and I can't return with PlotBand when I click on the legend again.
I couldn't find how to remove and put the PlotBand back on:
var a = 1,
b = 2,
c = 4,
d = 5,
dados = [[a, b, '#32CD32','manutenção','Mantuenção'], [c, d, '#FF0000','falha','Falha']];
Highcharts.chart('container', {
chart: {
zoomType: 'xy',
},
showInLegend: true,
title: {
text: 'Zoom Geral e Barras Verticais'
},
yAxis: {
scrollbar: {
enabled: true,
showFull: false
}
},
plotOptions: {
series: {
shadow:false,
borderWidth:0,
events:{
legendItemClick:function(e){
console.log(this.userOptions)
var ids = this.userOptions['id'];
console.log(ids)
if(this.userOptions['id'] === ids){
var hides = this.xAxis.plotLinesAndBands.filter(id => id.id == ids);
if(this.visible == true){
hides[0].svgElem.hide()
} else {
hides[0].svgElem.show()
}
}
}
}
}
},
series: [{
name:'Série A',
data: [-10, 31, 35, 40, 15, 10]
}
]
},
function(){
for (let i = 0; i < dados.length; i++ ){
this.xAxis[0].addPlotBand({
from: dados[i][0],
to: dados[i][1],
color: dados[i][2],
name: dados[i][4],
id: dados[i][3]
}, )
this.addSeries({
marker: {
symbol: 'square',
radius: 20},
name: dados[i][4],
color: dados[i][2],
type: 'scatter',
id: dados[i][3]
}, )
}
}
)
The plot bands are not hidden because the chart is redrawn after the event. You can return false from the callback function, but all plot bands will be visible after another redraw. Example: http://jsfiddle.net/BlackLabel/m2xrjhLk/
I recommend you to remove/add the plot band on legend click:
plotOptions: {
series: {
...,
events: {
legendItemClick: function(e) {
var id = this.options.id;
if (this.visible) {
this.xAxis.removePlotBand(id);
} else {
this.xAxis.addPlotBand(plotBands.find(
plotBand => plotBand.id === id
));
}
}
}
}
}
Live demo: http://jsfiddle.net/BlackLabel/n1jgew27/
Related
I have simplified my graph below for demo purposes but i have a lot of categories but not all series names will have a value of those categories. So when i select that series name how would i go about making 0 value categories disappear.
For example below when selecting person 1 the service 1 category should disappear instead of remain with no bars for it
Highcharts.chart('container', {
chart : {type: 'column'},
xAxis: {
categories: ["service1", "service2", "service3", "service"] ,
showEmpty : true ,
ordinal: false
},
series: [{
name: 'person1',
data: [0,2,3],
},
{ name : 'person2',
data: [10,6,5]
}]
});
link to the code https://jsfiddle.net/uroepk1j/
ppotaczek's Code from JSFiddle
Highcharts.chart('container', {
chart: {
type: 'column',
ignoreHiddenSeries: true
},
plotOptions: {
column: {
pointPlacement: null,
events: {
legendItemClick: function() {
var points = this.data,
hideCategory = false,
breaks = [],
stop,
series = this.chart.series;
this.chart.xAxis[0].update({
breaks: []
});
this.visible = !this.visible;
points.forEach(function(p, i) {
stop = false;
series.forEach(function(s) {
if (!stop && (!s.visible || s.data[i].y === 0)) {
hideCategory = true;
} else {
stop = true;
hideCategory = false;
}
}, this);
if (hideCategory) {
breaks.push({
from: i - 0.5,
to: i + 0.5,
breakSize: 0
})
}
hideCategory = false;
}, this);
this.visible = !this.visible;
this.chart.xAxis[0].update({
breaks: breaks
});
}
}
},
},
xAxis: {
categories: ['Col 1', 'Col 2', 'Col 3']
},
series: [{
name: 'person1',
data: [2, 0, 3],
},
{
name: 'person2',
data: [10, 1, 5]
}
]
});
Thanks for your help
You can use broken-axis module and insert breaks in place of the category in which there are no points, for example:
plotOptions: {
column: {
grouping: false,
pointPlacement: null,
events: {
legendItemClick: function() {
if (!this.visible) {
breaks[this.index] = {}
this.chart.xAxis[0].update({
breaks: breaks
});
} else {
breaks[this.index] = {
from: this.xData[0] - 0.5,
to: this.xData[0] + 0.5,
breakSize: 0
}
this.chart.xAxis[0].update({
breaks: breaks
});
}
}
}
},
}
Live demo: http://jsfiddle.net/BlackLabel/4utq7e3n/
API Reference: https://api.highcharts.com/highcharts/xAxis.breaks
I'm having trouble aligning tooltip position on bar chart. Tooltip positioning works fine if all charts are line chart but not with bar/column charts. Could the Highcharts experts please help me with this issue?
http://jsfiddle.net/yhenwtsb/1/
var charts = [],
options1, options2;
function syncTooltip(container, p) {
var i = 0;
for (; i < charts.length; i++) {
if (container.id != charts[i].container.id) {
if (charts[i].tooltip.shared) {
charts[i].tooltip.refresh([charts[i].series[0].data[p]]);
} else {
charts[i].tooltip.refresh(charts[i].series[0].data[p]);
}
}
}
}
options1 = {
chart: {
type: "column",
inverted: true
},
plotOptions: {
series: {
point: {
events: {
mouseOver: function() {
syncTooltip(this.series.chart.container, this.x - 1);
}
}
}
}
},
xAxis: {
type: 'datetime'
}
};
options2 = {
plotOptions: {
series: {
point: {
events: {
mouseOver: function() {
syncTooltip(this.series.chart.container, this.x - 1);
}
}
}
}
},
xAxis: {
type: 'datetime'
}
};
charts[0] = new Highcharts.Chart($.extend(true, {}, options1, {
chart: {
renderTo: 'container1',
marginLeft: 40, // Keep all charts left aligned
},
tooltip: {
shared: true,
crosshairs: true,
valueDecimals: 2
},
series: [{
data: [
[1, 29.9],
[2, 71.5],
[3, 106.4]
]
}, {
data: [
[1, 59.9],
[2, 91.5],
[3, 136.4]
]
}]
}));
charts[1] = new Highcharts.Chart($.extend(true, {}, options2, {
chart: {
renderTo: 'container2',
marginLeft: 40, // Keep all charts left aligned
},
tooltip: {
shared: false
},
series: [{
data: [
[1, 29.9],
[2, 71.5],
[3, 106.4]
]
}]
}));
You can set tooltip.followPointer to true and add a small plugin to synchronize the second chart:
(function(H) {
H.wrap(H.Pointer.prototype, 'runPointActions', function(proceed, e, p) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
var tooltipPos = this.chart.tooltip.now;
Highcharts.charts.forEach(function(chart) {
if (chart !== this.chart) {
chart.tooltip.updatePosition({
plotX: tooltipPos.x - chart.plotLeft,
plotY: tooltipPos.y - chart.plotTop
});
}
}, this);
});
}(Highcharts));
Live demo: http://jsfiddle.net/BlackLabel/2w9683gb/
API Reference: https://api.highcharts.com/highcharts/tooltip.followPointer
Docs: https://www.highcharts.com/docs/extending-highcharts
$('#container').bind('mousemove touchmove touchstart', function (e) {
var chart,
point,
i,
event;
for (i = 0; i < Highcharts.charts.length; i = i + 1) {
chart = Highcharts.charts[i];
event = chart.pointer.normalize(e.originalEvent);
point = chart.series[0].searchPoint(event, true);
if (point) {
point.highlight(e);
}
}
});
Highcharts.Pointer.prototype.reset = function () {
return undefined;
};
Highcharts.Point.prototype.highlight = function (event) {
event = this.series.chart.pointer.normalize(event);
this.onMouseOver(); // Show the hover marker
this.series.chart.tooltip.refresh(this); // Show the tooltip
this.series.chart.xAxis[0].drawCrosshair(event, this); // Show the crosshair
};
function syncExtremes(e) {
var thisChart = this.chart;
if (e.trigger !== 'syncExtremes') { // Prevent feedback loop
Highcharts.each(Highcharts.charts, function (chart) {
if (chart !== thisChart) {
if (chart.xAxis[0].setExtremes) { // It is null while updating
chart.xAxis[0].setExtremes(
e.min,
e.max,
undefined,
false,
{ trigger: 'syncExtremes' }
);
}
}
});
}
}
// Get the data. The contents of the data file can be viewed at
$.getJSON(
'https://cdn.rawgit.com/highcharts/highcharts/v6.0.4/samples/data/activity.json',
function (activity) {
$.each(activity.datasets, function (i, dataset) {
// Add X values
dataset.data = Highcharts.map(dataset.data, function (val, j) {
return [activity.xData[j], val];
});
$('<div class="chart">')
.appendTo('#container')
.highcharts({
chart: {
marginLeft: 40, // Keep all charts left aligned
spacingTop: 20,
spacingBottom: 20
},
title: {
text: dataset.name,
align: 'left',
margin: 0,
x: 30
},
credits: {
enabled: false
},
legend: {
enabled: false
},
xAxis: {
crosshair: true,
events: {
setExtremes: syncExtremes
},
labels: {
format: '{value} km'
}
},
yAxis: {
title: {
text: null
}
},
tooltip: {
positioner: function () {
return {
// right aligned
x: this.chart.chartWidth - this.label.width,
y: 10 // align to title
};
},
borderWidth: 0,
backgroundColor: 'none',
pointFormat: '{point.y}',
headerFormat: '',
shadow: false,
style: {
fontSize: '18px'
},
valueDecimals: dataset.valueDecimals
},
series: [{
data: dataset.data,
name: dataset.name,
type: dataset.type,
color: Highcharts.getOptions().colors[i],
fillOpacity: 0.3,
tooltip: {
valueSuffix: ' ' + dataset.unit
}
}]
});
});
}
);
Any help will be appreciated
You can hide unwanted x axes using visible property:
xAxis: {
visible: i === 2,
(...)
},
Live demo: http://jsfiddle.net/BlackLabel/abf07jgc/
API reference: https://api.highcharts.com/highcharts/xAxis.visible
Link to JFiddle: http://jsfiddle.net/z24ysp8m/3/
Here is the code in concern:
$(function() {
var chartData = [-5, 5, -10, -20];
var timeStamps = [];
var index = 1;
var pWidth = 25;
$('#b').click(function(){
timeStamps.push(new Date());
var buttonB = document.getElementById('b');
buttonB.disabled = true;
/* if(index == 1){
$('#container').highcharts().xAxis[0].labels.style = {"color":"#6D869F","fontWeight":"bold"};
}*/
if(index <= chartData.length){
$('#container').highcharts().series[0].remove();
$('#container').highcharts().addSeries({pointPlacement: 'on', data: [chartData[index - 1]],
pointWidth: pWidth});
$('#container').highcharts().xAxis[0].setCategories([index]);
setTimeout(function(){index++;}, 2000);
}
if(index < chartData.length){
setTimeout(function(){buttonB.disabled = false;}, 1500);
}else{
setTimeout(function(){buttonB.style.visibility="hidden";}, 1500);
}
if(index == chartData.length - 1){
setTimeout(function(){document.getElementById('b').innerHTML = 'Lasst Period';}, 1500);
}
console.log(timeStamps);
})
// $(document).ready(function () {
Highcharts.setOptions({
lang: {
decimalPoint: ','
},
});
$('#container').highcharts({
chart: {
type: 'column',
width: 170,
marginLeft: 74,
marginRight: 16,
marginBottom: 60
},
title: {
text: ''
},
colors: [
'#0000ff',
],
xAxis: {
title: {
text: ''
// offset: 23
},
gridLineWidth: 1,
startOnTick: true,
tickPixelInterval: 80,
categories: ['Filler'], // used only to make sure that the x-axis of the two charts
// are aligned, not shown on the chart via setting the font color to white
min:0,
max:0,
labels: {
style: {
color: 'white'
}
}
},
yAxis: {
title: {
text: 'Value'
},
min: -20,
max: 20,
tickPixelInterval: 40
},
plotOptions: {
series: {
animation: {
duration: 1000
}
}
},
credits: {
enabled: false
},
tooltip: {
formatter: function () {
return Highcharts.numberFormat(this.y, 2) + '%';
}
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
data: [],
pointWidth: pWidth
}]
});
// });
});
I want that the x-axis has no label when the page is loaded (The reason why I added in a filler text with white font is due to the fact that I don't want the size of the chart change upon click of a button). And upon the click of button, the label should be consecutively 1, 2, 3, 4...
Is there anyway around it except for setting marginBottom (which is not very precise)?
You may use .css() method for changing fill color of your text label.
Here you can find information about this method:
http://api.highcharts.com/highcharts#Element.css
Highcharts.each($('#container').highcharts().xAxis[0].labelGroup.element.children, function(p, i) {
$(p).css({
fill: 'red'
});
});
And here you can find simple example how it can work:
http://jsfiddle.net/z24ysp8m/6/
Not sure why because I have done it in the past, but I have a Highcharts bar chart and it won't animate. This is the declaration of the chart,
function initializeData() {
$http.get(url).success(function(ret) {
$scope.jsondata = ret;
var newdata = [];
for (x = 0; x < 5; x++) {
newdata.push({
name: setName($scope.jsondata[x].name),
y: $scope.jsondata[x].data[0],
color: getColor($scope.jsondata[x].data[0])
});
}
$scope.chart.series[0].setData(newdata);
});
mainInterval = $interval(updateData, 5000);
}
function updateData() {
$http.get(url).success(function(ret) {
$scope.jsondata = ret;
console.debug("here");
for (x = 0; x < 5; x++) {
$scope.chart.series[0].data[x].update({
y: $scope.jsondata[x].data[0],
color: getColor($scope.jsondata[x].data[0])
});
}
});
}
$scope.chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'bar',
animation: true,
events: {
load: initializeData
}
},
title: {
text: ''
},
xAxis: {
type: 'category',
labels: {
style: {
fontSize: '11px'
}
}
},
yAxis: {
min: 0,
max: 100,
title: {
text: 'Total Score',
align: 'high'
}
},
legend: {
enabled: false
},
tooltip: {
pointFormat: 'Total Score <b>{point.y:.3f}</b>'
},
series: [{
name: 'Active Users',
data: [],
dataLabels: {
enabled: true,
rotation: 30,
style: {
fontSize: '10px',
fontFamily: 'Verdana, sans-serif'
},
format: '{point.y:.3f}', // one decimal
}
}]
});
And as you can see I have animate : true, so I am not sure what is the problem here. I have this older plunker where all of the data is in separate series, but it animates fine. But this is the plunker I am working on and having trouble with. They are like identical basically. In the newer one I broke out the initialization of data into its own method, but that is the only real main difference.
Some edits:
So as I was saying, I have done things this way with an areaspline chart (I know it was said they work a bit different but they are set up identically).
function initializeData() {
$interval.cancel(mainInterval);
$scope.previousPackets = '';
$http.get("https://api.myjson.com/bins/nodx").success(function(returnedData) {
var newdata = [];
var x = (new Date()).getTime();
for (var step = 9; step >= 0; step--) {
newdata.push([x - 1000 * step, 0]);
}
$scope.chart.series[0].setData(newdata);
});
mainInterval = $interval(updateData, 2000);
}
function updateData() {
$http.get(url + acronym + '/latest').success(function(returnedData) {
var x = (new Date()).getTime();
if ($scope.previousPackets != returnedData[0].numPackets) {
$scope.chart.series[0].addPoint([x, returnedData[0].numPackets], true, true);
$scope.previousPackets = returnedData[0].numPackets;
} else {
$scope.chart.series[0].addPoint([x, 0], true, true);
}
});
}
$scope.chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'areaspline',
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load: initializeData
}
},
title: {
text: ''
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Packets'
},
plotLines: [{
value: 0,
width: 1,
color: '#d9534f'
}]
},
tooltip: {
formatter: function() {
return Highcharts.numberFormat(this.y) + ' packets<b> | </b>' + Highcharts.dateFormat('%H:%M:%S', this.x);
}
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
name: 'Packets',
data: []
}]
});
I also updated the first chunk of code with the initializeData() method and updateData() method which are seemingly identical in both different charts.
It looks like it plays an important role if you provide your data at chart initialization or after. For simplicity I refactored your code a little
function initializeChart(initialData, onload) {
$scope.chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'bar',
animation: true,
events: {
load: onload
}
....
series: [{
name: 'Active Users',
data: initialData,
dataLabels: {
enabled: true,
format: '{point.y:.3f}', // one decimal
}
}]
});
}
function getData(callback) {
$http.get(url).success(function(ret) {
$scope.jsondata = ret;
var newdata = [];
for (x = 0; x < 5; x++) {
newdata.push([setName(ret[x].name), ret[x].data]);
}
callback(newdata);
});
}
As a result your two planks are in essense reduced to two methods below. The first initializes chart with preloaded data and the second updates data in existing chart.
function readDataFirst() {
getData(function(newdata) {
initializeChart(newdata);
});
}
function initializeChartFirst() {
initializeChart([], function() {
getData(function(newdata) {
$scope.chart.series[0].setData(newdata);
})
});
}
The first one animates fine while the second does not. It looks like highcharts skips animation if dataset is not initial and is treated incompatible.
However if you really want to have animation in your current plant (chart first workflow) you can achieve that by initializing first serie with zeros and then with the real data. This case it will be treated as update
function forceAnimationByDoubleInitialization() {
getData(function(newdata) {
initializeChart([]);
var zerodata = newdata.map(function(item) {
return [item[0], 0]
});
$scope.chart.series[0].setData(zerodata);
$scope.chart.series[0].setData(newdata);
});
All these options are available at http://plnkr.co/edit/pZhBJoV7PmjDNRNOj2Uc