We run a data visualizations product that needs to create richly detailed tooltips in order to communicate lots of information. Here is an example of our product's graph and tooltip:



GitClear tooltip using Google Charts

Our problem was that we needed the tooltips to be able to fall outside the main graph, because they were too big to conveniently fit in the graph, especially at narrow page widths. This means that we had to use the following set of Google Chart options for our tooltip:

tooltip: { ignoreBounds: true, isHtml: true, trigger: 'both' },


All of which is good and fine, until one gets their cursor close to the edge of the screen, when the tooltip ends up popping off the left side of the screen.


linkAdvanced positioning of a Google Charts tooltip

To work around this, we need to be able to set the position of our tooltip through an event that can look at where the tooltip was going to pop, and allow ourselves to adjust that. There are a few Google results that claim to allow this, but they relied on tricks like fastening the tooltip to a particular area in the chart, rather than allowing it to be positioned near the cursor (and the user's attention). Here was the listener that we had to set up to get a callback to when a tooltip is popping up:


google.visualization.events.addListener(exports.chartWrapper, 'ready', function() {
google.visualization.events.addListener(exports.chartWrapper.getChart(), 'onmouseover', function(e) {
GitClear.UI.HistoricalImpactTooltip.setMouseoverTooltipPosition(exports.chartContainer,
exports.chartWrapper.getDataTable(), e);
});
});


The chartWrapper here is a Google Charts ChartWrapper class, which conveniently bundles together the data and view for a chart. If you don't use chartWrapper, then your chart object can be passed to the listener directly.


The next problem is that the onMouseOver event for Google Charts, as of 2020, will only pass a row/column that the user was mousing over. We need to somehow translate the row hovered in the Google Chart into a left position on the HTML page. To some extent, the manner in which you translate the data coordinates to visual location is application-dependent. Here was how we handled it:


exports.setMouseoverTooltipPosition = function(chartContainer, dataTable, event) {
if (event.row === undefined) return;
const dateRowHovered = event.row;
const tooltip = chartContainer.find('.google-visualization-tooltip');
if (tooltip.length) {
// If the tooltip wants to pop out in non-visible area, let's just not:
if (tooltip.offset().left < 0) {
var tableLeftPercent = (dateRowHovered / dataTable.getNumberOfRows());
var tableInnerArea = chartContainer.innerWidth();
var chartPadding = (chartContainer.innerWidth() - tableInnerArea) * 0.5;
const leftPosition = chartPadding + (tableInnerArea * tableLeftPercent);
// "20" means we'll position this 20 pixels right of the user's hovered data point (instead of right on top of it, which would be undesirable)
tooltip.css("left", (leftPosition + 20) + "px");
}
}
};


Another option would be to use a utility that tracks the user's mouse location via Javascript, which would eliminate the need to do all the work on translating the hovered table/row into a visual data point.


Hope this helps somebody who wants to use Google Charts advanced tooltips but is having trouble getting them positioned in a satisfactory location. This method isn't quite perfect, but it was the best that I could muster in a couple hours of Googling.