Synchronized tooltips positioning with line and horizontal bar charts - javascript

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

Related

plotBands Hide and Show parameters do not work in Highcharts

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/

Highcharts inactive state must be applied to all items except for hovered item

With reference to the following
jsfiddle https://jsfiddle.net/2f17L4at/
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Disabled inactive state'
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr']
},
plotOptions: {
series: {
states: {
inactive: {
enabled: true
}
}
}
},
series: [{
data: [1, 3, 2, 4]
}, {
data: [5, 3, 4, 2]
}]
});
When I hover over series1 in jan, the series1 for other months are also highlighted. I only want series for the hovered month to be highlighted and everything else should be in inactive state. Can anyone help me with how to achieve that?
You can loop through points from the active series and set their state to inactive:
plotOptions: {
series: {
point: {
events: {
mouseOver: function() {
const point = this;
point.series.points.forEach(p => {
if (p !== point) {
p.setState('inactive');
}
});
},
mouseOut: function() {
const point = this;
point.series.points.forEach(p => {
if (p !== point) {
p.setState('normal');
}
});
}
}
},
...
}
}
Live demo: https://jsfiddle.net/BlackLabel/fsdw21em/
API Reference: https://api.highcharts.com/class-reference/Highcharts.Point#setState

Hide a category when a series name has no value attributed to it highchart

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

Simplify JavaScript array variable

I'm looking to simplify this code. Any way to it so? Spring MVC + Apex Charts
var d = /*[[${s0}]]*/ null`; <-- It is sent via the Spring Framework. Basically represents datetime(in millis) at `d[0]`, `d[3]`,... Temperature at `d[1]`, `d[4]`,... and Humidity at `d[2]`, `d[5]`,...
<script type="text/javascript" th:inline="javascript">
var d = /*[[${s0}]]*/ null;
var options = {
chart: {
type: 'area',
height: 300
},
series: [
{
name: 'Temperature',
data: [
[d[0], d[1]],
[d[3], d[4]],
[d[6], d[7]],
[d[9], d[10]],
[d[12], d[13]],
[d[15], d[16]],
[d[18], d[19]],
[d[21], d[22]],
[d[24], d[25]],
[d[27], d[28]],
[d[30], d[31]],
[d[33], d[34]],
[d[36], d[37]],
[d[39], d[40]],
[d[42], d[43]],
[d[45], d[46]],
[d[48], d[49]],
[d[51], d[52]],
[d[54], d[55]],
[d[57], d[58]],
[d[60], d[61]],
[d[63], d[64]],
[d[66], d[67]],
[d[69], d[70]]
]
},
{
name: "Humidity",
data: [
[d[0], d[2]],
[d[3], d[5]],
[d[6], d[8]],
[d[9], d[11]],
[d[12], d[14]],
[d[15], d[17]],
[d[18], d[20]],
[d[21], d[23]],
[d[24], d[26]],
[d[27], d[29]],
[d[30], d[32]],
[d[33], d[35]],
[d[36], d[38]],
[d[39], d[41]],
[d[42], d[44]],
[d[45], d[47]],
[d[48], d[50]],
[d[51], d[53]],
[d[54], d[56]],
[d[57], d[59]],
[d[60], d[62]],
[d[63], d[65]],
[d[66], d[68]],
[d[69], d[71]]
]
}
],
xaxis: {
type: 'datetime'
},
yaxis: [
{
axisTicks: {
show: true
},
axisBorder: {
show: true,
},
title: {
text: "Temperature"
}
}, {
min: 0,
max: 100,
opposite: true,
axisTicks: {
show: true
},
axisBorder: {
show: true,
},
title: {
text: "Humidity"
}
}
],
legend: {
position: 'top',
horizontalAlign: 'center'
},
tooltip: {
x: {
format: 'HH:mm dd/MM/yy'
},
}
}
var chart = new ApexCharts(document.querySelector("#chart0"), options);
chart.render();
</script>
I just need to simplify sending data via d[0], d[1] etc. Is there any kind of loop or anything else I can use?
You could take a function which takes the data and a pattern for the wanted elements and an offset for increment for the next row.
function mapByPattern(data, pattern, offset) {
var result = [], i = 0;
while (i < data.length) {
result.push(pattern.map(j => data[i + j]));
i += offset;
}
return result;
}
var data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
result = { series: [
{ name: 'Temperature', data: mapByPattern(data, [0, 1], 3) },
{ name: "Humidity", data: mapByPattern(data, [0, 2], 3) }
]};
console.log(result);
Thank You, Nina. Code code didn't work exactly as i wanted but was so helpful to fix my own. Thanks alot! Here's some fixed code :)
var data = /*[[${s0}]]*/ null;
function mapByPattern(data, pattern, offset) {
var result = [], i = 0;
while (i < data.length) {
result.push(pattern.map(j => data[i + j]));
i += offset;
}
return result;
}
var options = {
chart: {
type: 'area',
height: 300
},
series: [
{
name: 'Temperature',
data: mapByPattern(data, [0, 1], 3)
},
{
name: "Humidity",
data: mapByPattern(data, [0, 2], 3)
}
],
xaxis: {
type: 'datetime'
},
yaxis: [
{
axisTicks: {
show: true
},
axisBorder: {
show: true,
},
title: {
text: "Temperature"
}
}, {
min: 0,
max: 100,
opposite: true,
axisTicks: {
show: true
},
axisBorder: {
show: true,
},
title: {
text: "Humidity"
}
}
],
legend: {
position: 'top',
horizontalAlign: 'center'
},
tooltip: {
x: {
format: 'HH:mm dd/MM/yy'
},
}
}
var chart = new ApexCharts(document.querySelector("#chart0"), options);
chart.render();

Highcharts Custom SVG Marker Symbol is Shaped Different in Legend

the custom SVG marker symbol I have drawn is rendered differently in the legend than in the chart. I have drawn the marker that I need for the chart but in the legend, the symbol has a thin line to the left.
I have attached a picture below and will include the code, I have spent too much time on this and don't have anyone to ask on this topic. If anyone can help me out, it would be greatly appreciated.
function renderChart(data, startRange, endRange) {
// Create custom marker
Highcharts.SVGRenderer.prototype.symbols.lineBar = function (x, y, w, h) {
return ['M', x + w / 2, y + h / 2, 'L', x + w + 10, y + h / 2, 'z'];
};
if (Highcharts.VMLRenderer) {
Highcharts.VMLRenderer.prototype.symbols.lineBar = Highcharts.SVGRenderer.prototype.symbols.lineBar;
}
var chart = Highcharts.chart({
chart: {
renderTo: 'system-load-scheduler',
type: 'line',
},
navigation: {
buttonOptions: {
enabled: false
}
},
title: {
text: ''
},
yAxis: {
min: 0,
title: {
text: 'Tasks'
},
labels: {
style: {
color: 'blue'
}
},
categories: generateCategories(data),
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: {
day: '%b %d'
},
title: {
text: 'Date'
}
},
tooltip: {
headerFormat: '<b>{series.name}</b><br>',
pointFormat: 'Scheduled {point.x:%b. %e} at {point.x:%l:%M%P}'
},
plotOptions: {
line: {
marker: {
enabled: true
}
},
series: {
cursor: 'pointer',
stickyTracking: false,
marker: {
states: {
hover: {
radiusPlus: 0,
lineWidthPlus: 1,
halo: {
size: 0
}
}
}
},
states: {
hover: {
halo: {
size: 0
}
}
}
}
},
legend: {
enabled: true,
symbolPadding: 20
},
series: generateSeries(data, startRange, endRange)
});
chart.yAxis[0].labelGroup.element.childNodes.forEach(function (label) {
label.style.cursor = 'hand';
label.onclick = function () {
var idx = ctrl.allTaskNames.indexOf(this.textContent);
renderTaskInfo(ctrl.data[idx]);
ctrl.scheduler.taskIdx = idx;
ctrl.backService.saveObject(CTRL_DASHBOARD_SCHEDULER_STR, ctrl.scheduler);
};
});
return chart;
}
You can erase the line with just some CSS code
.highcharts-legend .highcharts-graph {
display:none;
}
Fiddle

Categories

Resources