I'm using the syncExtremes function to synchronise multiple charts (and their zooms) as implemented in the example below:
https://jsfiddle.net/pz2bak0u
However I've come across two issues I can't seem to find my way around. The first one is that if you zoom multiple times in different charts without reseting and then click on one of the Reset zoom buttons, the other Reset zoom buttons hang around (and I would like them to disappear as they don't have any functionality when clicked).
I then modified the fiddle to allow xy zooming:
https://jsfiddle.net/47dz68Lt
If you zoom in on the first chart, then the second, and then the third, and try unzooming the first chart, the other two stay halfway zoomed.
I've tried
chart.xAxis[0].setExtremes(null, null);
but haven't had any luck.
Is it even posible to reset all xy zooming in all charts at the same time as in the x zooming example?
Currently you reset only x-axes extremes, with xy zoom type, you need to also reset y-axes extremes:
var H = Highcharts;
H.wrap(H.Chart.prototype, 'zoomOut', function(proceed) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
H.charts.forEach(function(chart) {
if (chart !== this) {
chart.yAxis[0].setExtremes(null, null, undefined, false);
}
}, this);
});
To control the display of the resetZoomButton, you can wrap showResetZoom method, for example:
H.wrap(H.Chart.prototype, 'showResetZoom', function(proceed) {
var visibleBtn = false;
H.charts.forEach(function(chart) {
if (chart.resetZoomButton) {
visibleBtn = true;
}
});
if (!visibleBtn) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
}
});
Live demo: https://jsfiddle.net/BlackLabel/bg7L1nev/
Docs: https://www.highcharts.com/docs/extending-highcharts/extending-highcharts
Related
I have a Highcharts instance that is rendered within a scrollable container. The tooltip.outside option is also set to true so that the tooltip is always on top regardless if it fits the chart svg.
When you scroll however, you can see tooltip following the scrolling. Moreover, when you hover over the series, the tooltip renders in various positions.
Is there a way to fix it? FYI if you set tooltip.outside to false everything works just fine. I'm sure the issue revolves around the fact that when set to true, the calculation to determine where to render the tooltip is no longer correct as the position changed with the scrolling.
So to sum up the 2 issues that appear:
The tooltip follows the scrolling
On re-hovering, the position on the tooltip on the series is wrong.
See gif around the issue: https://imgur.com/a/Zj3NstL
See example: https://jsfiddle.net/tcqeo415/2/
If you comment out my CSS code in the example, you should be able to see how it should work
This answer is pretty similar to #ppotaczek one in terms of the idea just a different implementation.
When the mouse enters a chart, the chart position is cached so if you scroll but your mouse is still within the chart, it doesn't recalculate the position but due to the scrolling the position has changed.
A solution would be to use tooltip.positioner and disable the caching by nullifying the chartsPosition.
positioner: function (w, h, point) {
this.chart.pointer.chartPosition = null;
return this.getPosition(w, h, point);
},
This will force the chart to recalculate the charts position. Note that this might be DOM expensive.
Then, if you want to maintain the scrolling behaviour checkout #ppotaczeck's answer. The code below will remove any tooltips on scroll (works only for the first example)
document.body.addEventListener("scroll", function() {
Highcharts.charts[0].tooltip.hide(0);
}, true)
Example: https://jsfiddle.net/gv1m6tjy/
A chart does not recalculate it's position because it does not track the scroll event.
To fix this, you can recalculate chart.pointer.chartPosition.top. Below code works only for the first chart:
(function (H) {
H.wrap(H.Tooltip.prototype, 'refresh', function (proceed, points) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
this.points = points;
});
}(Highcharts));
document.getElementById('container1').addEventListener('mouseenter', function(){
var chart = Highcharts.charts[0];
if (chart && chart.pointer && !chart.startChartPosY) {
chart.pointer.getChartPosition();
chart.startChartPosY = chart.pointer.chartPosition.top;
}
});
document.getElementById('outer').addEventListener('scroll', function(e){
var H = Highcharts,
chart = H.charts[H.hoverChartIndex],
tooltip = chart.tooltip;
if (chart && chart.pointer) {
chart.pointer.chartPosition.top = chart.startChartPosY - this.scrollTop;
}
if (tooltip && !tooltip.isHidden) {
tooltip.refresh(tooltip.points);
}
});
Live demo: https://jsfiddle.net/BlackLabel/ao0c21g6/3/
Docs: https://www.highcharts.com/docs/extending-highcharts/extending-highcharts
I'm trying to implement synchronised charts in my application using the example code from Highcharts here:
https://www.highcharts.com/demo/synchronized-charts
I have a column layout using the Materialize framework and the charts are positioned side by side in a row. After doing some playing around with the Highcharts example, it seems the charts don't behave the same horizontally as they do vertically. The labels are not synced across the charts and the crosshairs don't move in sync either.
After doing some reading I've found a similar question has been asked before:
Highcharts Sync charts horizontally
However, as someone pointed out in the comments on the "marked correct" answer, the solution doesn't work for responsive charts like mine. This person was advised to ask a separate SO question, but as I can't find it, I'm asking it.
So far this is the closest I can get to my charts properly working:
https://jsfiddle.net/6h7aL2rw/1/
However, as you can see as you move the cursor to the end of the first chart, the tooltip and crosshairs are not fully synchronised and are behind by a few points on each chart.
The code I've changed from the original example is the following:
$('#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); // Find coordinates within the chart
event.chartX = (event.chartX + 3 * $('.chart').width()) % $('.chart').width();
point = chart.series[0].searchPoint(event, true); // Get the hovered point
if (point) {
point.highlight(e);
}
}
});
As was pointed out in the original question though, I don't understand the significance of this:
event.chartX + 3 * $('.chart').width()
But I feel that it's this bit of code that is the problem preventing the charts from being in sync.
That problem is caused by the gaps between the charts, please check an example without them: https://jsfiddle.net/BlackLabel/5Lgrc08z/
As a solution you can set event.chartX to e.offsetX:
$('#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); // Find coordinates within the chart
event.chartX = e.offsetX;
point = chart.series[0].searchPoint(event, true); // Get the hovered point
if (point) {
point.highlight(e);
}
}
});
Live demo: https://jsfiddle.net/BlackLabel/ts7w4kxu/
I need to dynamically sync the xAxis crosshairs across multiple HighStocks charts.
The example http://jsfiddle.net/BlackLabel/hh90ps4c/28/ demonstrates how to sync the controls inside one chart. I cloned the demo into this http://jsfiddle.net/jakobvinther/ayf5gst2/ ...and replaced the single chart by a table with two charts. The JavaScript code was almost just duplicated for the second chart.
Out of the box, zooming, panning and the rangeSelector sliders in the two charts are nicely synced (I did not change any code to achieve that).
The problem is that the xAxis crosshairs in the two charts are not synced, they work inside each chart individually. How can that be done?
/* thanks */
If the charts are not in one column, the problem is the mouse event x coordinate. You can refer to the first chart in the column to get the coordinates you need:
$('#container1').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];
// Find coordinates within the chart
event = Highcharts.charts[0].pointer.normalize(e);
// Get the hovered point
point = chart.series[0].searchPoint(event, true);
if (point) {
point.highlight(e);
}
}
});
Live demo: http://jsfiddle.net/BlackLabel/8krwuof9/
I am working on a web application displaying multiple charts over the same categories. If you want an example, think population for different cities over time. The charts all have crosshairs enabled for the X axis.
I've been asked if, when a user hovers over a chart and moves the mouse - therefore moving the crosshairs - it is possible to have the crosshairs of all the other charts as well, in parallel, kinda mirroring the movement.
Off the top of my head it should not be impossible - capture the position on the X axis whenever the mouse is moved, then set/move the crosshairs on all charts to the same value for all the other charts - but that would work only if moving crosshairs programmatically is possible.
Is this possible in a non trivial way?
Edit: I created a partially working version on jsfiddle, based on the jsfiddle linked to in the answer, and replicating the two column layout of the charts in our web app: http://jsfiddle.net/basiliosz/sgc8jg34/
The crosshairs are moved only in charts directly above and below the one where the user is hovering the mouse cursor, but not in the other column. This is the crucial code snippet from the event handler:
for (i = 0; i < noOfCharts; i = i + 1) {
chart = separateCharts[i]
event = chart.pointer.normalize(e.originalEvent); // Find coordinates within the chart
point = chart.series[0].searchPoint(event, true); // Get the hovered point
if (point) {
point.highlight(event);
}
}
where Point.prototype.highlight is defined as this:
Highcharts.Point.prototype.highlight = function (ev) {
this.onMouseOver(); // Show the hover marker
this.series.chart.tooltip.refresh(this); // Show the tooltip
this.series.chart.xAxis[0].drawCrosshair(ev, this); // Show the crosshair
};
There is an internal function for drawing crosshair which can be used (it is not part of the official API)
/**
* Draw the crosshair
*
* #param {Object} e The event arguments from the modified pointer event
* #param {Object} point The Point object
*/
Axis.prototype.drawCrosshair: function(e, point) {}
example: http://jsfiddle.net/6rbhxmrp/
You can also the official demo with synchronizing charts https://www.highcharts.com/demo/synchronized-charts
The example with a disabled tooltip: http://jsfiddle.net/vwm4oe6k/
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