google charts timelines element border-radius - javascript

I have a problem figuring out how to set the border-radius on elements in a google charts Timeline. I have looked through all the options but there doesn't seem to be one for that.
I have tried manually setting it but without any luck.
Does anyone have a solution to this problem?
thx in advance

the chart elements can be modified when the chart's 'ready' event fires
however, the chart will revert back to the original style on any interactivity
a MutationObserver can be used to know when the chart has been modified
in order to re-apply the custom style / border radius
the chart is drawn using svg, to change the border radius on a rect element,
set attributes 'rx' and 'ry'
see the following working snippet...
google.charts.load('current', {
callback: drawChart,
packages: ['timeline']
});
function drawChart() {
var container = document.getElementById('timeline');
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({ type: 'string', id: 'President' });
dataTable.addColumn({ type: 'date', id: 'Start' });
dataTable.addColumn({ type: 'date', id: 'End' });
dataTable.addRows([
[ 'Washington', new Date(1789, 3, 30), new Date(1797, 2, 4) ],
[ 'Adams', new Date(1797, 2, 4), new Date(1801, 2, 4) ],
[ 'Jefferson', new Date(1801, 2, 4), new Date(1809, 2, 4) ]
]);
var observer = new MutationObserver(setBorderRadius);
google.visualization.events.addListener(chart, 'ready', function () {
setBorderRadius();
observer.observe(container, {
childList: true,
subtree: true
});
});
function setBorderRadius() {
Array.prototype.forEach.call(container.getElementsByTagName('rect'), function (rect) {
if (parseFloat(rect.getAttribute('x')) > 0) {
rect.setAttribute('rx', 20);
rect.setAttribute('ry', 20);
}
});
}
chart.draw(dataTable);
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="timeline"></div>

Related

Google Chart Timeline Tooltip will not stay open on click [duplicate]

I need to trigger the Timeline chart tooltip on selection instead of hover. This doesn't seem to work.
I get my tooltips if I have this in the chart options:
tooltip: { isHtml: true, trigger: 'focus' }
But if I change it to this:
tooltip: { isHtml: true, trigger: 'selection' }, the tooltips don't show up when I click the timeline bars.
Is this supposed to be possible with the Timeline chart? I can't find anything in the docs to say that it isn't supported, although I might have missed something...
The only supported trigger which will open a tooltip in a Timeline-chart is focus
Possible workaround:
google.setOnLoadCallback(drawVisualization);
function drawVisualization() {
var container = document.getElementById('timeline');
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({
type: 'string',
id: 'President'
});
dataTable.addColumn({
type: 'date',
id: 'Start'
});
dataTable.addColumn({
type: 'date',
id: 'End'
});
dataTable.addRows([
['Washington', new Date(1789, 3, 30), new Date(1797, 2, 4)],
['Adams', new Date(1797, 2, 4), new Date(1801, 2, 4)],
['Jefferson', new Date(1801, 2, 4), new Date(1809, 2, 4)]
]);
//select-handler
google.visualization.events.addListener(chart, 'select', function(e) {
//the built-in tooltip
var tooltip = document.querySelector('.google-visualization-tooltip:not([clone])');
//remove previous clone when there is any
if (chart.ttclone) {
chart.ttclone.parentNode.removeChild(chart.ttclone)
}
//create a clone of the built-in tooltip
chart.ttclone = tooltip.cloneNode(true);
//create a custom attribute to be able to distinguish
//built-in tooltip and clone
chart.ttclone.setAttribute('clone', true);
//inject clone into document
tooltip.parentNode.insertBefore(chart.ttclone, chart.tooltip);
});
chart.draw(dataTable, {tooltip: {isHtml: true }});
}
.google-visualization-tooltip {
opacity: 0 !important;
max-width: 200px !important;
}
.google-visualization-tooltip[clone] {
opacity: 1 !important;
}
html,
body,
timeline {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
<script type="text/javascript" src="https://www.google.com/jsapi?autoload={'modules':[{'name':'visualization','version':'1.1','packages':['timeline']}]}"></script>
<div id='timeline' style="height:90%"></div>
It still uses the default-behaviour(tooltip on focus).
But the built-in tooltip is hidden(via CSS)
In the select-handler it fetches the built-in tooltip out of the document(it's hidden, but it's still there) and creates a clone which will be injected into the document.

How to insert complex code using DOM manipulation

I am still learning javascript.
Here is the site I am playing with. http://keysoft.keydesign-themes.com/demo1/
I want to insert google visualization chart code using DOM manipulation (insertAdjacentHTML) into this site.
Here is the code of google's timeline chart:
google.charts.load('current', {'packages':['timeline']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var container = document.getElementById('timeline');
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({ type: 'string', id: 'President' });
dataTable.addColumn({ type: 'date', id: 'Start' });
dataTable.addColumn({ type: 'date', id: 'End' });
dataTable.addRows([
[ 'Washington', new Date(1789, 3, 30), new Date(1797, 2, 4) ],
[ 'Adams', new Date(1797, 2, 4), new Date(1801, 2, 4) ],
[ 'Jefferson', new Date(1801, 2, 4), new Date(1809, 2, 4) ]]);
chart.draw(dataTable);
}
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<div id="timeline" style="height: 180px;"></div>
I tried the code below in Chrome's console in order to insert it into this site but I think there are a lot of mistakes. Could you help me please how to improve the code below?
document.getElementsByClassName('container')[5].getElementsByClassName('row')[0].insertAdjacentHTML('afterend', '<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<div id="timeline" style="height: 180px;"></div>
google.charts.load('current', {'packages':['timeline']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var container = document.getElementById('timeline');
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({ type: 'string', id: 'President' });
dataTable.addColumn({ type: 'date', id: 'Start' });
dataTable.addColumn({ type: 'date', id: 'End' });
dataTable.addRows([
[ 'Washington', new Date(1789, 3, 30), new Date(1797, 2, 4) ],
[ 'Adams', new Date(1797, 2, 4), new Date(1801, 2, 4) ],
[ 'Jefferson', new Date(1801, 2, 4), new Date(1809, 2, 4) ]]);
chart.draw(dataTable);
}');
You can turn this into a string literal by using back ticks (`) instead of single quotes. And to ensure it doesn't jam up on the script tags you will want to change the closing script tag inside the literal to
<\/script>
I don't know the rest of your HTML structure so there may also be some issues with selecting the elements you want to place the tag inside.
Here's a complete example of a simple page that works for me:
</head>
<body>
<div id="container" class='container'>
<div class="row"></div>
</div>
</body>
<script>'use strict';
document.getElementsByClassName('container')[0].getElementsByClassName('row')[0].insertAdjacentHTML('afterend', `<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"><\/script>
<div id="timeline" style="height: 180px;"></div>\n\n <script> google.charts.load(\'current\', {\'packages\':[\'timeline\']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var container = document.getElementById(\'timeline\');
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({ type: \'string\', id: \'President\' });
dataTable.addColumn({ type: \'date\', id: \'Start\' });
dataTable.addColumn({ type: \'date\', id: \'End\' });
dataTable.addRows([
[ \'Washington\', new Date(1789, 3, 30), new Date(1797, 2, 4) ],
[ \'Adams\', new Date(1797, 2, 4), new Date(1801, 2, 4) ],
[ \'Jefferson\', new Date(1801, 2, 4), new Date(1809, 2, 4) ]]);
chart.draw(dataTable);
}<\/script>`);
</script>
I changed the number of containers from 5 to 0 for simplicity's sake but in the browser this runs without issue and injects the JS code after the row element. The back ticks (`) are important .

Moving annotations on Bar Chart with Negative Values Google Chart

I am using google charts within an MVC project.
I am looking to implement a bar chart that has negative values.
I would like the annotations on the negative portion of the chart to be on the same side as the end of the bar (just like the positive, see image below, green box is where I would like annotations to be).
I cant seem to find any documentation on how this can be achieved.
Is it possible to move the annotation to the other side?
there are no standard config options that will move the annotations
but you can move them manually
however, the chart will actually move them back whenever activity occurs,
such as on bar hover
have to use a MutationObserver, or something, to keep them there
use chart methods --> getChartLayoutInterface().getXLocation(value)
to find the location
also, need to adjust the axis window to leave room for the labels
see following working snippet...
google.charts.load('current', {
callback: function () {
var data = new google.visualization.DataTable({
cols: [
{label: 'x', type: 'string'},
{label: 'y0', type: 'number'},
],
rows: [
{c:[{v: 'Omega'}, {v: -0.95}]},
{c:[{v: 'Large'}, {v: -0.92}]},
{c:[{v: 'Medium'}, {v: 2.76}]},
{c:[{v: 'Tiny'}, {v: 2.03}]}
]
});
var options = {
annotations: {
alwaysOutside: true,
stem: {
color: 'transparent'
},
textStyle: {
color: '#000000'
}
},
hAxis: {
// leave room for annotation
viewWindow: {
min: data.getColumnRange(1).min - 1
}
},
legend: {
position: 'none'
}
};
var view = new google.visualization.DataView(data);
view.setColumns([0, 1, {
calc: 'stringify',
sourceColumn: 1,
type: 'string',
role: 'annotation'
}]);
var container = document.getElementById('chart');
var chart = new google.visualization.BarChart(container);
// move annotations
var observer = new MutationObserver(function () {
$.each($('text[text-anchor="start"]'), function (index, label) {
var labelValue = parseFloat($(label).text());
// only negative -- and -- not on tooltip
if ((labelValue < 0) && ($(label).attr('font-weight') !== 'bold')) {
var bounds = label.getBBox();
var chartLayout = chart.getChartLayoutInterface();
$(label).attr('x', chartLayout.getXLocation(labelValue) - bounds.width - 8);
}
});
});
observer.observe(container, {
childList: true,
subtree: true
});
chart.draw(view, options);
},
packages: ['corechart']
});
<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="chart"></div>

Google chart timeline horizontal scroll

I have a timeline chart, very similar to the very first example at this page (https://developers.google.com/chart/interactive/docs/gallery/timeline).
I have activities on the Y axis (making lunch, eating, ecc) and on the X axis i have the time.
I want to enable horizontal scroll and chart zoom in/out (As mentioned in this topic Google chart horizontal scrollbar). But i can't seem to get it working.
is there some way to enable horizontal scrolling on the timeline chart?
Many thanks.
Alessandro
there are no standard configuration options on the Timeline chart for scroll nor zoom.
but you could use css for horizontal scroll
set a specific width in the chart options --> width: 1200
and wrap it in a container with a smaller width and --> overflow-x: scroll;
see following working snippet for an example...
google.charts.load('current', {
callback: drawChart,
packages: ['timeline']
});
function drawChart() {
var container = document.getElementById('chart_div');
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({ type: 'string', id: 'President' });
dataTable.addColumn({ type: 'date', id: 'Start' });
dataTable.addColumn({ type: 'date', id: 'End' });
dataTable.addRows([
[ 'Washington', new Date(1789, 3, 30), new Date(1797, 2, 4) ],
[ 'Adams', new Date(1797, 2, 4), new Date(1801, 2, 4) ],
[ 'Jefferson', new Date(1801, 2, 4), new Date(1809, 2, 4) ]]);
chart.draw(dataTable, {
width: 1200
});
}
#chart_wrapper {
overflow-x: scroll;
overflow-y: hidden;
width: 400px;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_wrapper">
<div id="chart_div"></div>
</div>

Google Timeline Chart: Color each bar individually when multiple on "same" line

The Google Timeline charts seem to suggest coloring individual blocks on the timeline per the documentation:
https://google-developers.appspot.com/chart/interactive/docs/gallery/timeline#ControllingColors
But there seems to be a problem when two bars "overlap" on the same line, as you can see in this fiddle:
http://jsfiddle.net/7A88H/21/
Here is the key code:
dataTable.addRows([
[ 'red/green/blue', 'NAME OF BAR (should be RED) (ff0000)', new Date(1789, 3, 29), new Date(1797, 2, 3) ],
[ 'red/green/blue', 'NAME OF BAR (should be GREEN) (00ff00)', new Date(1796, 2, 3), new Date(1801, 2, 3) ],
[ 'red/green/blue', 'NAME OF BAR (should be BLUE) (0000ff)', new Date(1801, 2, 3), new Date(1809, 2, 3) ]]);
var options = {
colors: ['#ff0000', '#00ff00', '#0000ff'],
};
I tried playing with the accepted answer from this question by adding a 5th column (the color) to my data rows:
Google Charts API: Add Blank Row to Timeline?
Specifically, here is the function I thought I might be able to hijack to build my hack:
(function(){ //anonymous self calling function to prevent variable name conficts
var el=container.getElementsByTagName("rect"); //get all the descendant rect element inside the container
var width=100000000; //set a large initial value to width
var elToRem=[]; //element would be added to this array for removal
for(var i=0;i<el.length;i++){ //looping over all the rect element of container
var cwidth=parseInt(el[i].getAttribute("width"));//getting the width of ith element
if(cwidth<width){ //if current element width is less than previous width then this is min. width and ith element should be removed
elToRem=[el[i]];
width=cwidth; //setting the width with min width
}
else if(cwidth==width){ //if current element width is equal to previous width then more that one element would be removed
elToRem.push(el[i]);
}
}
for(var i=0;i<elToRem.length;i++)
elToRem[i].setAttribute("fill","none"); //make invisible all the rect element which has minimum width
})();
The hope was to grab each rect (skipping the bounding ones) and filling them (with a third loop, at the end) with their appropriate colors, but I couldn't figure out how to get their associated color (which was in the row objects) from the rect objects themselves.
I think you will need to use the additional options:
timeline: { groupByRowLabel: false }
Because, if you go to the g-page: https://google-developers.appspot.com/chart/interactive/docs/gallery/timeline#BarsOneRow in the Bars in One Row section they show how Presidents DON'T overlap, so you can't use it in this case, but for the method you are using it, timelines do overlap so they must be in their own row. It would probably be hard to read overlapping titles anyhow.
Side note: I noticed what google is doing. It's assigning the colors left to right, then wrapping. The titles however, are not wrapping, they just go left to right. Here is a fiddle I made: https://jsfiddle.net/camp185/2Lopnnt3/2/ to show how wrapping of colors working...added more colors.
function drawChart() {
var container = document.getElementById('example5.4');
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({ type: 'string', id: 'Role' });
dataTable.addColumn({ type: 'string', id: 'Name' });
dataTable.addColumn({ type: 'date', id: 'Start' });
dataTable.addColumn({ type: 'date', id: 'End' });
dataTable.addRows([
[ 'red/green/blue', 'NAME OF BAR (should be RED) (ff0000)', new Date(1789, 3, 29), new Date(1797, 2, 3) ],
[ 'red/green/blue', 'NAME OF BAR (should be GREEN) (00ff00)', new Date(1796, 2, 3), new Date(1801, 2, 3) ],
[ 'red/green/blue', 'NAME OF BAR (should be BLUE) (0000ff)', new Date(1801, 2, 3), new Date(1809, 2, 3) ]]);
var options = {
colors: ['#ff0000', '#00ff00', '#0000ff'],
timeline: { groupByRowLabel: false }
};
chart.draw(dataTable, options);
}
google.load('visualization', '1', {packages:['timeline'], callback: drawChart});
<script src="https://www.google.com/jsapi?.js"></script>
<div id='example5.4'></div>
Hope this will help you:
google.load("visualization", "1", {packages:["timeline"]});
google.setOnLoadCallback(drawChart);
// google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var container = document.getElementById('example5.4');
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({ type: 'string', id: 'Role' });
dataTable.addColumn({ type: 'string', id: 'Name' });
dataTable.addColumn({ type: 'date', id: 'Start' });
dataTable.addColumn({ type: 'date', id: 'End' });
dataTable.addRows([
[ 'red/green/blue', 'NAME OF BAR (should be RED) (ff0000)', new Date(1789, 3, 30), new Date(1797, 2, 4) ],
[ 'red/green/blue', 'NAME OF BAR (should be GREEN) (00ff00)', new Date(1797, 2, 4), new Date(1801, 2, 4) ],
[ 'red/green/blue', 'NAME OF BAR (should be BLUE) (0000ff)', new Date(1801, 2, 4), new Date(1809, 2, 4) ]
]);
// dataTable.addRows([
// [ 'red/green/blue', 'NAME OF BAR (should be RED) (ff0000)', new Date(1789, 3, 29), new Date(1797, 2, 3) ],
// [ 'red/green/blue', 'NAME OF BAR (should be GREEN) (00ff00)', new Date(1796, 2, 3), new Date(1801, 2, 3) ],
// [ 'red/green/blue', 'NAME OF BAR (should be BLUE) (0000ff)', new Date(1801, 2, 3), new Date(1809, 2, 3) ]]);
var options = {
colors: ['#ff0000', '#00ff00', '#0000ff'],
};
chart.draw(dataTable, options);
}
<script src="https://www.google.com/jsapi?.js"></script>
<div id='example5.4'></div>
There are two ways to do what you want, both of which change the background color based on the title you give the row. With your current code, when you hover over one of the rows, it displays information about that row. However, when leaving the hover, it redraws the box, making it much more complicated. I have done it both ways for you:
JSFiddle with Interactivity disabled (much simpler.. unfortunately I did this one after I did the complicated one)
JSFiddle with Interactivity enabled and messy setTimeOut functions (doesn't always work)
Here's the code when interactivity is disabled:
function drawChart() {
var container = document.getElementById('example5.4');
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({ type: 'string', id: 'Role' });
dataTable.addColumn({ type: 'string', id: 'Name' });
dataTable.addColumn({ type: 'date', id: 'Start' });
dataTable.addColumn({ type: 'date', id: 'End' });
dataTable.addRows([
[ 'red/green/blue', 'SHORT TEXT GREEN(00ff00)', new Date(1796, 2, 3), new Date(1801, 2, 3) ],
[ 'red/green/blue', 'NAME OF BAR (should be BLUE) (0000ff)', new Date(1801, 2, 3), new Date(1809, 2, 3) ],
[ 'red/green/blue', 'NAME OF BAR (should be RED) (ff0000)', new Date(1789, 3, 29), new Date(1797, 2, 3) ]]);
// dataTable.addRows([]);
var options = {
colors: ['#000', '#000', '#000'], // assign black background to each row
enableInteractivity: false //interactivity disabled
};
chart.draw(dataTable, options);
var elements = document.getElementsByTagName("text");
for(var el in elements)
{
if(typeof colorArray[elements[el].innerHTML] != "undefined")
{
setColor(elements[el],colorArray[elements[el].innerHTML]);
}
}
}
google.load('visualization', '1', {packages:['timeline'], callback: drawChart});
var colorArray = []; // format: colorArray["TEXT IN ROW"] = "COLOR"
colorArray["NAME OF BAR (should be RED) (ff0000)"] = "#ff0000";
function setColor(elem,newColor)
{
var rect = elem.previousSibling;
var rTitle = rect.innerHTML;
rect.setAttribute("fill",newColor);
}
NOTE: if the page is too narrow and text in a row is shortened with an ellipsis (...), this method fails because it is based off of the text in each row.
-- Old answer with no hack --
After looking into it further, it appears a simple way to fix this is by shortening the text of the green bar, because even when adding a new, separate row, the text for the green box doesn't fit inside of the box.
Additionally, it is processing the colors by line, so the Blue bar is getting the green color since the Green bar is being pushed to a new line. I would add any rows that will make a new line in a separate statement just to make it map out more logically, although it doesn't make a difference what order the arrays are in.
Here's the JSFiddle

Categories

Resources