Hover on google chart acting weird on firefox - javascript

When adding a hover event to google chart svg element the results are different between firefox and chrome (chrome is doing what I would expect).
What I would like to achieve is the ability for the user to hover on a chart and not care how close he is to the line - the hover should be smooth and easy to use.
Here is the relevant plunker: https://plnkr.co/edit/2IF2BnX0tS2wznAUr5bs?p=preview
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
<script src="https://www.gstatic.com/charts/loader.js"></script>
</head>
<body>
<script>
google.charts.load('current', {
callback: drawChart,
packages: ['corechart']
});
function drawChart() {
var dataTable = new google.visualization.DataTable({
cols: [
{ id: 'x', label: 'Num', type: 'number' },
{ id: 'y', label: 'Fn', type: 'number' }
]
});
for (var i = 0; i < 1000; i++) {
var xValue = { v: i };
var yValue = { v: i };
// add data row
dataTable.addRow([
xValue,
yValue
]);
}
var container = document.getElementById('chart_div');
var chart = new google.visualization.ChartWrapper({
chartType: 'LineChart',
dataTable: dataTable,
options: {
hAxis: {
gridlines: {
color: 'transparent'
},
title: 'Hover here is also fine'
},
tooltip: {
trigger: "none"
},
vAxis: {
gridlines: {
color: 'transparent'
},
title: 'Hover here is OK'
}
}
});
// add hover line
google.visualization.events.addOneTimeListener(chart, 'ready', function () {
var svgParent = container.getElementsByTagName('svg')[0];
var layout = chart.getChart().getChartLayoutInterface();
var lineHeight = layout.getBoundingBox('chartarea').height - 18;
var lineTop = layout.getBoundingBox('chartarea').top;
var hoverLine = container.getElementsByTagName('rect')[0].cloneNode(true);
hoverLine.setAttribute('y', lineTop);
hoverLine.setAttribute('height', lineHeight);
hoverLine.setAttribute('width', '1');
hoverLine.setAttribute('stroke', 'none');
hoverLine.setAttribute('stroke-width', '0');
hoverLine.setAttribute('fill', '#cccccc');
hoverLine.setAttribute('x', 0);
svgParent.appendChild(hoverLine);
svgParent.addEventListener("mousemove", function (e) {
hoverLine.setAttribute('x', e.offsetX);
});
});
chart.draw(container);
}
</script>
<div id="chart_div"></div>
Hover on the chart with chrome to see the expected behaiour.
Hover on the chart with firefox to see the issue.
Any idea how to solve this? is this a google chart bug? Am I adding an event listener to the wrong element?

It seems that the children tags in your svg are capturing the mousemove events (which makes since due to event propogation).
Simply add a pointer-events:none to those children elements (I recommended to use an id for that svg element)
svg *{
pointer-events: none;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
<script src="https://www.gstatic.com/charts/loader.js"></script>
</head>
<body>
<script>
google.charts.load('current', {
callback: drawChart,
packages: ['corechart']
});
function drawChart() {
var dataTable = new google.visualization.DataTable({
cols: [
{id: 'x', label: 'Num', type: 'number'},
{id: 'y', label: 'Fn', type: 'number'}
]
});
for (var i = 0; i < 1000; i++) {
var xValue = { v: i };
var yValue = { v: i };
// add data row
dataTable.addRow([
xValue,
yValue
]);
}
var container = document.getElementById('chart_div');
var chart = new google.visualization.ChartWrapper({
chartType: 'LineChart',
dataTable: dataTable,
options: {
hAxis: {
gridlines: {
color: 'transparent'
},
title: 'Hover here is also fine'
},
tooltip: {
trigger: "none"
},
vAxis: {
gridlines: {
color: 'transparent'
},
title: 'Hover here is OK'
}
}
});
// add hover line
google.visualization.events.addOneTimeListener(chart, 'ready', function () {
var svgParent = container.getElementsByTagName('svg')[0];
var layout = chart.getChart().getChartLayoutInterface();
var lineHeight = layout.getBoundingBox('chartarea').height - 18;
var lineTop = layout.getBoundingBox('chartarea').top;
var hoverLine = container.getElementsByTagName('rect')[0].cloneNode(true);
hoverLine.setAttribute('y', lineTop);
hoverLine.setAttribute('height', lineHeight);
hoverLine.setAttribute('width', '1');
hoverLine.setAttribute('stroke', 'none');
hoverLine.setAttribute('stroke-width', '0');
hoverLine.setAttribute('fill', '#cccccc');
hoverLine.setAttribute('x', 0);
svgParent.appendChild(hoverLine);
svgParent.addEventListener("mousemove", function(e) {
hoverLine.setAttribute('x', e.offsetX);
});
});
chart.draw(container);
}
</script>
<div id="chart_div"></div>
</body>
</html>

Related

Echarts: When [large: true] is enabled, the tooltip box will disappear when appenddata is dynamically updated

env
echarts version : 5.3.2
os: win11
issue
When using appenddata to update data, and the data volume exceeds the largethreshold, the tooltip will disappear when updating data, and the MouseMove of ecarts will pause for a moment
properties:
series: [
{
large:true,
largeThreshold:200
}
]
When the data point exceeds 200, the tooltip will disappear at [appenddata],
appendData code
myChart.appendData({
seriesIndex: 0,
data:[[Math.random()*12.2,Math.random()*12.4]]
}
);
myChart.resize();
The complete code is as follows
<!DOCTYPE html>
<html lang="zh-CN" style="height: 100%">
<head>
<meta charset="utf-8">
</head>
<body style="height: 100%; margin: 0">
<div id="container" style="height: 100%"></div>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts#5.3.2/dist/echarts.min.js"></script>
<script type="text/javascript">
var dom = document.getElementById('container');
var myChart = echarts.init(dom, null, {
renderer: 'canvas',
useDirtyRect: false
});
var option;
function getData() {
let temp = [];
for (let i = 0; i < 100000; i++) {
temp.push([
Math.random()*12.2,Math.random()*12.4
]);
}
return temp;
}
option = {
animation:false,
xAxis: {},
yAxis: {},
dataZoom:[
{
type: 'inside',
xAxisIndex: 0,
}
],
tooltip:{
annotation:false,
trigger: 'item'
},
series: [
{
type: 'scatter',
animation:false,
large:true,
largeThreshold:200,
progressive:1000000,
data: getData()
}
]
};
if (option && typeof option === 'object') {
myChart.setOption(option);
setInterval(()=>{
//appendData 时, toolTip框会消失
//When appendData, the toolTip box will disappear
myChart.appendData({
seriesIndex: 0,
data:[[Math.random()*12.2,Math.random()*12.4]]
}
);
myChart.resize();
},500)
myChart.on('mousemove', function(params) {
// 当appendData 时, 事件会停止一瞬间
// When appendData, the event stops for a moment
console.log("mousemove");
});
}
window.addEventListener('resize', myChart.resize);
</script>
</body>
</html>
Current issues
Expected effect

Adding different links to each column (Google Charts)

I need to add different links which open another page to every column on a column chart.
Clicks to every different columns should open another window.
My current template only works if columns are seperate from each other, i want to have it works even columns are contiguous.
My current template is that:
<html>
<head>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {
'packages': ['bar']
});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
['test', 'test', 'test', 'test', {
role: 'link'
}],
['test', 0.60, 0.62, 0.64, 'link'],
['test', 0.38, 0.53, 0.56, 'link'],
['test', 0.46800, 0.52100, 0.55500, 'link'],
['test', 0.43300, 0.49700, 0.53900, 'link']
]);
var options = {
title: 'test',
subtitle: 'test',
chartArea: {
width: '100%'
},
hAxis: {
title: 'test',
},
vAxis: {
title: 'test',
format: 'percent'
}
};
var chart = new google.charts.Bar(document.getElementById('columnchart_material'));
google.visualization.events.addListener(chart, 'select', function(e) {
var selection = chart.getSelection();
if (selection.length) {
var row = selection[0].row;
let link = data.getValue(row, 2);
window.open(link, '_blank');
}
});
chart.draw(data, google.charts.Bar.convertOptions(options));
}
</script>
</head>
<body>
<div id="columnchart_material" style="width: 800px; height: 500px;"></div>
</body>
</html>

Google visualization dual Y axis chart align line with specific bar

I have attached snippet for a dual Y axis chart.
The orange dot for Ontime% Goal corresponds with the blue bar for Ontime %. Both have been assigned to targetAxisIndex: 0
Can I shift/move the dot to align above the blue bar? (see attached picture for desired position).
Thank you as always to the experts out there!
google.charts.load('current', {'packages':['corechart', 'bar']});
google.charts.setOnLoadCallback(drawStuff);
function drawStuff() {
var button = document.getElementById('change-chart');
var chartDiv = document.getElementById('chart_div');
var data = google.visualization.arrayToDataTable([
['Type', 'Ontime%', 'Count', 'Ontime% Goal'],
['AE', 90, 500, 100]
]);
var classicOptions = {
width: 900,
series: {
0: {targetAxisIndex: 0, type: 'bars'},
1: {targetAxisIndex: 1, type: 'bars'},
2: {targetAxisIndex: 0, type: 'line', pointSize: 8, pointShape: { type: 'circle' } },
},
title: 'Ontime % on the left, Count on the right',
bar:{
width: "60%"
},
vAxis: {
minValue: 0
},
vAxes: {
// Adds titles to each axis.
0: {title: 'Ontime %'},
1: {title: 'Count'}
}
};
function drawClassicChart() {
var classicChart = new google.visualization.ColumnChart(chartDiv);
classicChart.draw(data, classicOptions);
button.innerText = 'Change to Material';
button.onclick = drawMaterialChart;
}
drawClassicChart();
};
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<br><br>
<div id="chart_div" style="width: 800px; height: 500px;"></div>
nothing out of the box will allow you to adjust the position of the point.
you can move it manually, on the chart's ready event.
but the chart will move it back when the user hovers the point.
you can use a MutationObserver to move the point when the chart moves it back,
but this will just cause it to blink from one spot to the other while it is being hovered.
not much you can do, unless you disable tooltips.
see following working snippet,
hover the point to see it move...
google.charts.load('current', {
packages: ['corechart']
}).then(function () {
//var button = document.getElementById('change-chart');
var chartDiv = document.getElementById('chart_div');
var data = google.visualization.arrayToDataTable([
['Type', 'Ontime%', 'Count', 'Ontime% Goal'],
['AE', 90, 500, 100]
]);
var classicOptions = {
width: 900,
series: {
0: {targetAxisIndex: 0, type: 'bars'},
1: {targetAxisIndex: 1, type: 'bars'},
2: {
targetAxisIndex: 0,
type: 'line',
pointSize: 8,
pointShape: {type: 'circle'},
},
},
title: 'Ontime % on the left, Count on the right',
bar:{
width: "60%"
},
vAxis: {
minValue: 0
},
vAxes: {
0: {title: 'Ontime %'},
1: {title: 'Count'}
}
};
function drawClassicChart() {
var classicChart = new google.visualization.ColumnChart(chartDiv);
google.visualization.events.addListener(classicChart, 'ready', function () {
var chartLayout = classicChart.getChartLayoutInterface();
var bounds = chartLayout.getBoundingBox('bar#0#0');
var observer = new MutationObserver(function () {
var circles = chartDiv.getElementsByTagName('circle');
if (circles.length > 1) {
circles[1].setAttribute('cx', (bounds.left + (bounds.width / 2)));
}
});
observer.observe(chartDiv, {
childList: true,
subtree: true
});
});
classicChart.draw(data, classicOptions);
//button.innerText = 'Change to Material';
//button.onclick = drawMaterialChart;
}
drawClassicChart();
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
best case, you could disable the chart's tooltips,
then add your own custom tooltips,
for both the point and columns, etc...
the chart does provide mouseover and mouseout events,
not sure its worth the effort...

Google chart only loads on refresh of page

Am a beginner using google charts/js. My google chart below loads fine, however there are times when the chart area loads blank html, and when I refresh the page it displays correctly.
I'm not sure why this is. It seems to do this on all browsers. This seems to indicate it could be with Mozilla, but the problem persists...
<script type="text/javascript">
google.charts.load('current', {
packages: ['corechart']
}).then(function drawChart() {
<?php
$imei = $bboxx_imei;
$result = shell_exec('Daily_Data_Retriever_ishackweb.py ' . $imei.' '. $end.' '.$start); #imei=sys.arg[1] end=sys.arg[2] start=sys.arg[3]
?>
var jsonData = <?php echo $result; ?> //{"Charge Current, (A)":{"2017-11-16T00:00:00.000Z":0.001373312,"2017-11-16T00:01:00.000Z":0.001373312,"2017-11-16T00:02:00.000Z":0.001373312,"2017-11-16T00:03:00.000Z":0.001373312,"2017-11-16T00:04:00.000Z":0.001373312},"Battery Voltage, (V)":{"2017-11-16T00:00:00.000Z":12.9267109178,"2017-11-16T00:01:00.000Z":12.9267109178,"2017-11-16T00:02:00.000Z":12.9267109178,"2017-11-16T00:03:00.000Z":12.9267109178,"2017-11-16T00:04:00.000Z":12.9267109178}};
var chartCols = ['Datetime'];
Object.keys(jsonData).forEach(function (column) {
chartCols.push(column);
});
// build list of date
var dateValues = [];
Object.keys(jsonData).forEach(function (column) {
Object.keys(jsonData[column]).forEach(function (dateValue) {
if (dateValues.indexOf(dateValue) === -1) {
dateValues.push(dateValue);
}
});
});
// build chart data
var chartData = [chartCols];
dateValues.forEach(function (dateValue) {
var row = [new Date(dateValue)];
Object.keys(jsonData).forEach(function (column) {
row.push(jsonData[column][dateValue] || null);
});
chartData.push(row);
});
var data = google.visualization.arrayToDataTable(chartData);
var options = {
chartArea: {width:'90%', height:'85%'},
//title: 'Battery Voltage and Panel Charge',
curveType: 'function',
legend: { position: 'bottom' },
vAxes: {0: {viewWindowMode:'explicit',
viewWindow:{
max:16,
min:11
},
gridlines: {color: 'transparent'},
},
1: {viewWindowMode:'explicit',
viewWindow:{
max:5,
min:0
},
},
},
series: {0: {targetAxisIndex:1},
1: {targetAxisIndex:0},
},
};
var chart = new google.visualization.LineChart(document.getElementById('line_chart'));
chart.draw(data, options);
$(window).resize(function(){
drawChart()
});
});
</script>
when using a function inline / anonymously, although you can provide a name,
you will not be able to call that same function again, by it's name
google.charts.load('current', {
packages: ['corechart']
}).then(function drawChart() { // <-- cannot call this again, no name needed
...
instead, declare the function separately, then pass a reference where needed,
using the name of the function
google.charts.load('current', {
packages: ['corechart']
}).then(drawChart);
$(window).resize(drawChart);
function drawChart() {
...
}
see following working snippet...
google.charts.load('current', {
packages: ['corechart']
}).then(drawChart);
$(window).resize(drawChart);
function drawChart() {
var jsonData = {"Charge Current, (A)":{"2017-11-16T00:00:00.000Z":0.001373312,"2017-11-16T00:01:00.000Z":0.001373312,"2017-11-16T00:02:00.000Z":0.001373312,"2017-11-16T00:03:00.000Z":0.001373312,"2017-11-16T00:04:00.000Z":0.001373312},"Battery Voltage, (V)":{"2017-11-16T00:00:00.000Z":12.9267109178,"2017-11-16T00:01:00.000Z":12.9267109178,"2017-11-16T00:02:00.000Z":12.9267109178,"2017-11-16T00:03:00.000Z":12.9267109178,"2017-11-16T00:04:00.000Z":12.9267109178}};
var chartCols = ['Datetime'];
Object.keys(jsonData).forEach(function (column) {
chartCols.push(column);
});
// build list of date
var dateValues = [];
Object.keys(jsonData).forEach(function (column) {
Object.keys(jsonData[column]).forEach(function (dateValue) {
if (dateValues.indexOf(dateValue) === -1) {
dateValues.push(dateValue);
}
});
});
// build chart data
var chartData = [chartCols];
dateValues.forEach(function (dateValue) {
var row = [new Date(dateValue)];
Object.keys(jsonData).forEach(function (column) {
row.push(jsonData[column][dateValue] || null);
});
chartData.push(row);
});
var data = google.visualization.arrayToDataTable(chartData);
var options = {
chartArea: {width:'90%', height:'85%'},
//title: 'Battery Voltage and Panel Charge',
curveType: 'function',
legend: { position: 'bottom' },
vAxes: {
0: {
viewWindowMode:'explicit',
viewWindow:{
max:16,
min:11
},
gridlines: {color: 'transparent'},
},
1: {
viewWindowMode:'explicit',
viewWindow:{
max:5,
min:0
},
},
},
series: {
0: {targetAxisIndex:1},
1: {targetAxisIndex:0},
},
};
var chart = new google.visualization.LineChart(document.getElementById('line_chart'));
chart.draw(data, options);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="line_chart"></div>

Javascript charts as input

I have a requirement as below.
I need to input time series data (day data) using a graph. The data is usually like below but the profile can be changed depending upon the situation.
Right now the data is being manually entered in textboxes which makes it very difficult.
I am wondering whether there are solutions that let user draw on a chart and generate the data in the back. In other words, the user picks certain points and the profile is drawn.
see following working snippet, click on the chart to add a data point...
google.charts.load('current', {
callback: function () {
var data = new google.visualization.DataTable({
"cols": [
{"label": "x", "type": "number"},
{"label": "y", "type": "number"}
]
});
var axisMax = 10;
var ticks = [];
for (var i = 0; i <= axisMax; i = i + 0.5) {
ticks.push(i);
}
var options = {
chartArea: {
bottom: 64,
height: '100%',
left: 64,
top: 24,
width: '100%'
},
hAxis: {
format: '0.0',
textStyle: {
fontSize: 9
},
ticks: ticks,
title: data.getColumnLabel(0),
viewWindow: {
min: 0,
max: axisMax
}
},
height: 600,
legend: {
position: 'top'
},
pointSize: 4,
vAxis: {
format: '0.0',
textStyle: {
fontSize: 9
},
ticks: ticks,
title: data.getColumnLabel(1),
viewWindow: {
min: 0,
max: axisMax
}
}
};
var tableDiv = document.getElementById('table_div');
var table = new google.visualization.Table(tableDiv);
var chartDiv = document.getElementById('chart_div');
var chart = new google.visualization.LineChart(chartDiv);
var chartLayout = null;
google.visualization.events.addListener(chart, 'ready', function () {
chartLayout = chart.getChartLayoutInterface();
});
google.visualization.events.addListener(chart, 'click', function (sender) {
data.addRow([
chartLayout.getHAxisValue(sender.x),
chartLayout.getVAxisValue(sender.y)
]);
drawChart();
});
function drawChart() {
chart.draw(data, options);
table.draw(data);
}
window.addEventListener('resize', drawChart, false);
drawChart();
},
packages:['corechart', 'table']
});
div {
text-align: center;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
<div id="table_div"></div>

Categories