Introduction
There should not be a problem putting multiple charts on a page, either displaying the same data in different charts or loading a new set of data in a new datatable. The Google Charts API documentation for multiple charts is fairly clear but there are a couple of gotchas.
Chart Loader
The chart loader must only be called once on a page. Calling it multiple times will result in problems.
<script src="https://www.gstatic.com/charts/loader.js"></script>
Chart Libraries
About half of the charts can be loaded a single library named "corechart"
google.charts.load('current', {'packages':['corechart']});
Charts loaded using corechart are: Area, Bar, Bubble, Candlestick, Column, Combo, Diff, Donut, Histogram, Intervals, Line, Pie, Scatter, Stepped Area, Trendline, and Waterfall
The exceptions are (package to be loaded in brackets): Annotation (annotationchart), Calendar (calendar), Gantt (gantt), Gauge (gauge) , Geo (geochart), Map (map), Org (orgchart), Sankey (sankey), Table (table), Timeline (timeline), Tree Map (treemap), Vega (vegachart), and Word Tree (wordtree)
Where multiple charts need to be loaded a single statement can be used, for example:
google.charts.load('current', {packages: ['corechart', 'table', 'sankey']};
If the chart to be drawn is not one of those included in the the corechart set then it does not need to be included, for example:
google.charts.load('current', {packages: ['timeline']};
Let the User Choose!
There are many ways to visualize data amd the designer must ensure the proper chart type is chosen to display the data. Sometimes it is better to give the user a choice which visualization to use to better understand the data.
The Google Charts API allows the redrawing of the data without importing it again. The following example uses a simple dropdown to allow the user to change the chart type.
Notes
The data comes from a Google Sheet. This is the documentation for the API Column Charts.
This example is not the same as the Controls and Dashboards in the API. Those are designed to filter data, this example changes the chart type drawn.
This chart is based on a column chart and the changes in the sub-type are made by changing the chart options.
This example uses the onChange event in the dropdown to trigger the redrawing of the chart. Some people may prefer to use an event listener. For more complex projects a single event listener can be used to listen for events anywhere within a element with an ID and distinguish which element inside it triggered the listener. Handling Events for Many Elements by Kirupa explains how to do this.
The code changes between three values for the column chart, false, true and 'percent'. If you just need to toggle between false and true then this simple code works for Boolean values:
variable = !variable;
I use namespaces in my JavaScript as per the W3C JavaScript Best Practices. This stops my code interfering with any libraries I may use and vice versa. Because redrawChart needs to accessed by the dropdown, I needed to make a global variable inside the namespace to hold the data table. I also had to return the redrawChart function at the end of the namespace to make that available to the dropdown.
The chart shows the sex of students in a small college for various semesters. By changing the chart type the various relationships and trends of the data can be seen more easily.
The HTML for the dropdown is:
<select id="chooseChart" onchange="studentSex.redrawChart()">
<option value="Column">Column</option>
<option value="Stacked">Stacked</option>
<option value="Percent">Percent</option>
</select>
The JavaScript for the above chart is:
<script type="text/javaScript">
var studentSex = (function() {
// create namespace global variable to hold the data
var studentData;
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var queryString = encodeURIComponent('SELECT B,C,D ORDER BY A');
var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/1tswaWUAbeBijHq4o505w0h7TmbD-qGhR3jBactcbGq0/gviz/tq?gid=1786091632&headers=1&tq=' + queryString);
query.send(handleQueryResponse);
}
function handleQueryResponse(response) {
if (response.isError()) {
alert('Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage());
return;
}
studentData = response.getDataTable();
redrawChart();
}
function redrawChart(){
var chartType = document.getElementById("chooseChart").value;
var optionVar = false;
if (chartType == "Stacked") {optionVar = true}
if (chartType == "Percent") {optionVar = 'percent'}
var options = {isStacked: optionVar};
var chart = new google.visualization.ColumnChart(document.getElementById('studentData_div'));
chart.draw(studentData, options);
}
// Make the redrawChart function available outside the studentSex namespace by returning it.
// This is so the onChange event of the dropdown can access it
return{redrawChart};
})();
</script>
In the following example the same data is used and it is the chart type that is changed.
Notes
Notice that because the code for this example is in a different JavaScript namepsace than the previous, the same variable and function names can be used and that they do not interfere with each other. This is the whole point of using namespaces. The scope of the functions and variables is confined to the namespace.
There is no easy way for variables to be used in the chart definitions and so the entire line is defined.
If the chartWrapper Class is used then the setChartType or setOptions methods can be used in the options or drawChart constructor to change the chart type and those can accept variables.
There is no reason changing both the chart type and the options for them cannot be used together. Care must be taken that the defaults are defined or odd behaviours may show or the chart may not draw at all.
Both Column and Bar charts use the corechart library so there is no need to load another. Some charts will require the loading of other chart libraries.
The HTML for the dropdown is:
<select id="chartType" onchange="changeChart.redrawChart()">
<option value="Column">Column</option>
<option value="Bar">Bar</option>
</select>
The JavaScript for the above chart is:
<script type="text/javaScript">
// create namespace global variable (changeChart) to hold the data
var changeChart = (function() {
var studentData;
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var queryString = encodeURIComponent('SELECT B,C,D ORDER BY A');
var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/1tswaWUAbeBijHq4o505w0h7TmbD-qGhR3jBactcbGq0/gviz/tq?gid=1786091632&headers=1&tq=' + queryString);
query.send(handleQueryResponse);
}
function handleQueryResponse(response) {
if (response.isError()) {
alert('Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage());
return;
}
studentData = response.getDataTable();
redrawChart();
}
function redrawChart(){
var chartType = document.getElementById("chartType").value;
if (chartType == "Column") {chart = new google.visualization.ColumnChart(document.getElementById('changeChart_div'));}
if (chartType == "Bar") {chart = new google.visualization.BarChart(document.getElementById('changeChart_div'));}
chart.draw(studentData);
}
// Make the redrawChart function available outside the changeChart namespace by returning it.
// This is so the onChange event of the dropdown can access it
return{redrawChart};
})();
</script>
Event Listeners
The above method is not the most efficient. What happens is that whenever the dropdown is changed the data is reread and the charts reproduced. A better method is to add an event listener to the dropdown and then use the data table already in memory to redraw the chart.
The following example can draw multiple charts, but because some are not very useful, the user is given the opportunity to rotate or transpose the data table. The checkbox to do this has its own event listener.
The HTML for the aboce chart is:
<select id="chartDrop">
<option value="Column">Column</option>
<option value="Bar">Bar</option>
<option value="Line">Line</option>
<option value="Area">Area</option>
</select>
<input type="checkbox" id="toggleRotate">
<label for="toggleRotate">Toggle the data rotation</label>
<div id="multi-graph"></div>
The JavaScript for the above chart is:
<script type="text/javascript">
var userChart = (function() {
google.charts.load('current', {packages: ['corechart']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var queryString = encodeURIComponent('SELECT B,C,D ORDER BY A');
var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/1tswaWUAbeBijHq4o505w0h7TmbD-qGhR3jBactcbGq0/gviz/tq?gid=1786091632&headers=1&tq=' + queryString);
query.send(handleQueryResponse);
}
function handleQueryResponse(response) {
if (response.isError()) {
alert('Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage());
return;
}
var originalData = response.getDataTable();
var chartType= "ColumnChart";
var drawData = originalData;
//Rotate originalData to rotatedData
var rotatedData = new google.visualization.DataTable();
rotatedData.addColumn('string', 'data label'); // change this label to whatever is appropriate for you
for (var i = 0; i < originalData.getNumberOfRows(); i++) {
rotatedData.addColumn('number', originalData.getValue(i, 0));
}
for (var i = 1; i < originalData.getNumberOfColumns(); i++) {
var row = [originalData.getColumnLabel(i)];
for (var j = 0; j < originalData.getNumberOfRows(); j++) {
row.push(originalData.getValue(j, i));
}
rotatedData.addRow(row);
checkchkbox();
checkdrop();
newCharts();
function newCharts(){
google.visualization.drawChart({
"containerId": "multi-graph",
"dataTable": drawData,
"chartType": chartType,
"options": {"width": 1000, "height": 300,"is3D": true, legend: { position: 'top', maxLines: 3 }, }
})
}
function checkchkbox(){
var getState = document.getElementById("toggleRotate");
if (getState.checked){
drawData = originalData;}
else {
drawData = rotatedData;}
newCharts();
}
function checkdrop(){
var dropValue = document.getElementById("chartDrop").value;
chartType = dropValue + "Chart";
newCharts();
}
// Add event handler to checkbox
var chkbox = document.getElementById("toggleRotate");
chkbox.addEventListener ("change", checkchkbox, false);
// Add event handler to dropdown
var drop = document.getElementById("chartDrop");
drop.addEventListener ("change", checkdrop, false);
}
}
})();
</script>
As usual with any sort of programming, there are multiple methods of doing the same thing. The above code uses two event listeners, but there are methods of applying a single event listener to multiple elements (Stack Overflow).
Multiple Data Tables, Multiple Charts
Executing the code and drawing charts from multiple tables has long been a source of frustration and the API's Help Forum and Stack Overflow has multiple questions about this. Such as Can't find the problem with my multiple charts on same page, Multiple Google Charts, More than two Google Charts on a single page?, Google Charts stops drawing after first chart and How to add two Google charts on the one page?
Those issues were largely fixed in the API and it is possible to create the data tables then draw each one as in the API Draw Multiple Charts documentation. This involves moving the code to draw the charts from the body of the page to the head section. But what happens if you have a lot of charts and prefer to use arrays and loops to draw them?
Apart from errors in the code such as trying to use loader.js twice, the problem is the way that JavaScript behaves. JavaScript is mostly synchronous, that is, it processes one line of code at a time - except for the times it is asynchronous and does multiple things at once!
An example of an asynchronous function is setTimeout. What happens is that the code up to the timer is executed, the timer starts, any remaining code is then executed until the timer reaches its elapsed time then that code executes. Sometimes what needs to happen is that the code execution needs to be paused completely until an event occurs, the screen is redrawn, a file opened and read or some other condition met.
The latter is what Callbacks, Promises, and Async/Await are all about. One of the eaiest tutorials I've found to understand all of this is Callbacks, Promises, and Async by Chris Nwamba on Scotch IO (Internet Archive). Just after this page was first written Scotch IO was aquired by DigitalOcean. The article was rewritten as Understanding the Event Loop, Callbacks, Promises, and Async/Await in JavaScript by Tania Rascia on DigitalOcean.
drawChart()
The drawChart constructor is barely anything but variables, so that provides a good way to import multiple data tables and draw multiple charts. There is a slight price to pay, as the documentation says "you cannot assign method listeners to catch chart events." If that turns out to be a problem then there's always the chartWrapper Class.
drawChart is very easy to use but consider the following. There are three divs to hold three different charts from three different sets of data. It should first draw a column chart, a bar chart and then a table but you are probably just seeing the table chart.
This is sometimes a problem when drawing multiple charts on a page. What I think is happening is that the FOR loop is being processed so fast that the first and second charts start to be processed but before they can be drawn the third chart code gets processed and takes over the screen drawing.
The JavaScript for the above chart(s) is:
<script type="text/javaScript">
var multiChart = (function() {
google.charts.load(['current','table']);
var myString = [];
myString[0] = '{"containerId": "column_div",' + '"dataSourceUrl": "https://docs.google.com/spreadsheets/d/1tswaWUAbeBijHq4o505w0h7TmbD-qGhR3jBactcbGq0/gviz/tq?gid=1786091632&headers=1",' + '"query":"SELECT B,C,D ORDER BY A",' + '"chartType": "ColumnChart"' + '}';
myString[1] = '{"containerId": "bar_div",' + '"dataSourceUrl": "https://docs.google.com/spreadsheets/d/1tswaWUAbeBijHq4o505w0h7TmbD-qGhR3jBactcbGq0/gviz/tq?gid=0&headers=1",' + '"query":"SELECT A,B ORDER BY A LIMIT 8",' + '"chartType": "BarChart"' + '}';
myString[2] = '{"containerId": "table_div",' + '"dataSourceUrl": "https://docs.google.com/spreadsheets/d/1tswaWUAbeBijHq4o505w0h7TmbD-qGhR3jBactcbGq0/gviz/tq?gid=234750873&headers=1",' + '"query":"SELECT A,B ORDER BY A",' + '"chartType": "Table"' + '}';
var strLength = myString.length;
for (i=0; i < strLength; i++){
var newString = myString[i];
function drawVisualization() {
google.visualization.drawChart(newString);
}
google.charts.setOnLoadCallback(drawVisualization);
}
})();
</script>
Solution: A Zero Length Timer
You should now be able to see the column, bar and table charts:
This is about the quickest and easiest way to draw simple multiple charts on a page and was done by including a setTimeout to queue the various graphs.
The JavaScript for the above chart(s) is:
<script type="text/javaScript">
var timerChart = (function() {
google.charts.load(['current','table']);
var myString = [];
myString[0] = '{"containerId": "timerColumn_div",' + '"dataSourceUrl": "https://docs.google.com/spreadsheets/d/1tswaWUAbeBijHq4o505w0h7TmbD-qGhR3jBactcbGq0/gviz/tq?gid=1786091632&headers=1",' + '"query":"SELECT B,C,D ORDER BY A",' + '"chartType": "ColumnChart"' + '}';
myString[1] = '{"containerId": "timerBar_div",' + '"dataSourceUrl": "https://docs.google.com/spreadsheets/d/1tswaWUAbeBijHq4o505w0h7TmbD-qGhR3jBactcbGq0/gviz/tq?gid=0&headers=1",' + '"query":"SELECT A,B ORDER BY A LIMIT 8",' + '"chartType": "BarChart"' + '}';
myString[2] = '{"containerId": "timerTable_div",' + '"dataSourceUrl": "https://docs.google.com/spreadsheets/d/1tswaWUAbeBijHq4o505w0h7TmbD-qGhR3jBactcbGq0/gviz/tq?gid=234750873&headers=1",' + '"query":"SELECT A,B ORDER BY A",' + '"chartType": "Table"' + '}';
var strLength = myString.length;
for (i=0; i < strLength; i++){
processChart(i);
}
function processChart(i) {
setTimeout(function() {
function drawVisualization() {
var newString = myString[i]
google.visualization.drawChart(newString);
}
google.charts.setOnLoadCallback(drawVisualization);
}, 0);
}
})();
</script>
What's happening in the above code is that the chart drawing is added to a timed queue of code to be executed. There is no other synchronous code to be run and the 0 time does not mean "do it now", it means "do it as soon as there's nothing else to be done" which is why using setTimeout does not guarantee accurate timings, but it does mean the code has to finish running before it starts on the next task.
Multiple Data Tables, Single Chart Area
This section of the page was written because of something that essasen and Arthur both asked in the API user-supported group and that is how do you draw multiple charts but swap in the same area using a button or other mechanism.
Method 1: Draw them all but toggle the display style
The API User Guide shows how to draw multiple charts on the same page. I changed the code slightly so that one of the divs is initially hidden. A button toggles the div's display style between "hidden" and "block".
The JavaScript to draw the charts is:
<script type="text/javaScript">
// Create a namespace
var pizzas = (function() {
// Load Charts and the corechart package.
google.charts.load('current', {'packages':['corechart']});
// Draw the pie chart for Sara's pizza when Charts is loaded.
google.charts.setOnLoadCallback(drawSaraChart);
// Draw the pie chart for the Tony's pizza when Charts is loaded.
google.charts.setOnLoadCallback(drawTonyChart);
// Callback that draws the pie chart for Sara's pizza.
function drawSaraChart() {
// Create the data table for Sara's pizza.
var data = new google.visualization.DataTable();
data.addColumn('string', 'Topping');
data.addColumn('number', 'Slices');
data.addRows([
['Mushrooms', 1],
['Onions', 1],
['Olives', 2],
['Zucchini', 2],
['Pepperoni', 1]
]);
// Set options for Sara's pie chart.
var options = {title:'How Much Pizza Sara Ate Last Night',
width:400,
height:300};
// Instantiate and draw the chart for sara's pizza.
var chart = new google.visualization.PieChart(document.getElementById('sara_chart_div'));
chart.draw(data, options);
}
// Callback that draws the pie chart for tony's pizza.
function drawTonyChart() {
// Create the data table for Tony's pizza.
var data = new google.visualization.DataTable();
data.addColumn('string', 'Topping');
data.addColumn('number', 'Slices');
data.addRows([
['Mushrooms', 2],
['Onions', 2],
['Olives', 2],
['Zucchini', 0],
['Pepperoni', 3]
]);
// Set options for Tony's pie chart.
var options = {title:'How Much Pizza Tony Ate Last Night',
width:400,
height:300};
// Instantiate and draw the chart for Tony's pizza.
var chart = new google.visualization.PieChart(document.getElementById('tony_chart_div'));
chart.draw(data, options);
}
})();
</script>
The HTML to draw the divs and button is:
<button onclick="showhidePizza()">Toggle Charts</button>
<div id="sara_chart_div" style="display:none;"></div>
<!-- <div id="sara_chart_div"></div> -->
<div id="tony_chart_div"></div>
The JavaScript that is called when the button is clicked on is:
<script language="javaScript">
function showhidePizza() {
var saraDiv = document.getElementById("sara_chart_div");
var tonyDiv = document.getElementById("tony_chart_div");
if (saraDiv.style.display === "none") {
saraDiv.style.display = "block";
tonyDiv.style.display = "none";
} else {
saraDiv.style.display = "none";
tonyDiv.style.display = "block";
}
}
</script>
Method 2: When a button is clicked, redraw the relevant chart
The inspiration for this code came from a near 7-year old question, "Change Google Chart Data and Appearance With Button" on Stack Overflow.
As WhiteHat, who answered the original question and who is a genius with Google Charts pointed out, the API uses a promise and because of that the setOnLoadCallback function is not always strictly needed. This may change though.
I altered WhiteHat's code a little. One thing I changed was the ability to use a single button to toggle between two charts, but I left two other buttons in the code to show an alternative method and how it can expanded to more than two datatables.
The JavaScript that is draws the charts and takes care of the button click is:
<script language="javaScript">
// Create a namespace
var speakers = (function() {
google.charts.load('current', {
'callback': function () {
var data1 = google.visualization.arrayToDataTable([
['Language', 'Speakers (in millions)'],
['Cat 1', 26], ['Cat 3', 16], ['Cat 2', 30]
]);
var data2 = google.visualization.arrayToDataTable([
['Language', 'Speakers (in millions)'],
['Cat 1', 100], ['Cat 3', 200], ['Cat 2', 300]
]);
var options = {
title: 'Indian Language Use',
legend: 'none',
pieSliceText: 'label',
slices: { 1: {offset: 0.2},
2: {offset: 0.3},
3: {offset: 0.4},
4: {offset: 0.5},
},
animation: {
startup:true,
duration: 1000,
easing: 'in'
}
};
var chart = new google.visualization.PieChart(document.getElementById('language_div'));
chart.draw(data1, options);
document.getElementById('toggleData').addEventListener('click', function () {
var btnValue = document.getElementById('toggleData').value;
if (btnValue == "0"){
chart.draw(data1, options);
btnValue = "1";}
else {
chart.draw(data2, options);
btnValue = "0";}
document.getElementById('toggleData').value = btnValue;
}, false);
document.getElementById('data1').addEventListener('click', function () {
chart.draw(data1, options);
}, false);
document.getElementById('data2').addEventListener('click', function () {
chart.draw(data2, options);
}, false);
},
'packages':['corechart']
});
})();
</script>
The HTML to draw the div and buttons is:
<button id="toggleData" value="1">Toggle Charts</button>
<input type="button" id="data1" value="Data1" />
<input type="button" id="data2" value="Data2" />
<div id="language_div"></div>