0
votes

I have this javascript code for Google Timeline chart but I can't fix the error.

This what supposed the chart should look like:

  1. Bar color depends on the course (e.g. IT)
  2. Tooltip has link
  3. HAxis will be placed above the chart
  4. HAxis will be replaced by string instead of dates (e.g. Oct. 6 to Week 1)

I think 1-3 were good but when I added #4 from @WhiteHat's suggestion, it contains an error(see snippet below).
I tried to fix it to no avail.
My javascript is knowledge is limited and is in beginner's level.

Could you please help me with this one and kindly explain to me on where I got it wrong.
Advise and suggestions are much appreciated.

Thanks in advance!


    google.charts.load("current", {packages:["timeline"], callback: drawChart});

function drawChart() {
var container = document.getElementById('chart_div');
var chart = new google.visualization.Timeline(container);

// hAxis put on top
google.visualization.events.addListener(chart, 'ready', afterDraw); 

// Link in tooltip
google.visualization.events.addListener(chart, 'select', function(e) { 
  var tooltip = document.querySelector('.google-visualization-tooltip:not([clone])');
  if (chart.ttclone) {
    chart.ttclone.parentNode.removeChild(chart.ttclone)
  }
  chart.ttclone = tooltip.cloneNode(true);
  chart.ttclone.setAttribute('clone', true);
  chart.ttclone.style.pointerEvents = 'auto';
  tooltip.parentNode.insertBefore(chart.ttclone, chart.tooltip);
});

var dataTable = new google.visualization.DataTable();
dataTable.addColumn({ type: 'string', id: 'Name' });
// for colorMap
dataTable.addColumn({ type: 'string', id: 'Course' }); 
dataTable.addColumn({ type: 'string', id: 'Subject' });
dataTable.addColumn({ type: 'string', id: 'ToolTip', role: 'tooltip', p:{html:true} });
dataTable.addColumn({ type: 'date', id: 'Start' });
dataTable.addColumn({ type: 'date', id: 'End' });
dataTable.addRows([
	
	// Timeline Start 
	['Student 1', 'ENGR', 'Trigonometry', '<a href="link_to_subj_desc" target="_blank">Trigonometry</a>', new Date(2016, 9, 30), new Date(2016, 10, 06)],
	['Student 2', 'IT', 'DB Management', '<a href="link_to_subj_desc" target="_blank">DB Management</a>', new Date(2016, 9, 30), new Date(2016, 10, 13)],
	['Student 3', 'CS', 'Introduction to Programming', '<a href="link_to_subj_desc" target="_blank">Introduction to Programming</a>', new Date(2016, 9, 30), new Date(2016, 10, 27)],
]);  
	
var colors = [];
var colorMap = {
	ENGR:	'#2ECC71',	// Green
	IT:	'#E67E22',	// Brown
	CS:	'#9B59B6',	// Violet
}

for (var i = 0; i < dataTable.getNumberOfRows(); i++) {
	colors.push(colorMap[dataTable.getValue(i, 1)]);
}

var rowHeight = 41;
var chartHeight = (dataTable.getNumberOfRows() + 1) * rowHeight;
	
var options = {
	timeline: { 
		groupByRowLabel: true,
		rowLabelStyle: {
			fontName: 'Century Gothic',
			fontSize: 14,
			color: '#333333',
			bold: 'true',
		},
		barLabelStyle: {
			fontName: 'Century Gothic',
fontSize: 11,
},
showRowLabels: true,
showBarLabels: true,
}, 
hAxis: {
minValue: new Date(2016, 9, 30),
maxValue: new Date(2017, 9, 28),
},
avoidOverlappingGridLines: true,
height: chartHeight,
width: '100%',
colors: colors,
};



// use a DataView to hide the category column from the Timeline
var view = new google.visualization.DataView(dataTable);
view.setColumns([0, 2, 3, 4, 5]);

// Change HAxis labels to Week
google.visualization.events.addListener(chart, 'ready', function () {
  var rowIndex = 0;      // data table row index
  var weekLabel = null;  // clone of text node - keep font settings, y-coord, etc...

  // remove haxis labels
  var labels = container.getElementsByTagName('text');
  while (labels.length > dataTable.getNumberOfRows()) {
    // ignore "category" labels
    if (dataTable.getFilteredRows([{column: 5, value: labels[labels.length - 1].innerHTML}]).length === 0) {
      if (weekLabel === null) {
        weekLabel = labels[labels.length - 1].cloneNode(true);
      }
      labels[labels.length - 1].parentNode.removeChild(labels[labels.length - 1]);
    }
  }

  

    // use timeline bars to find x coordinate for week labels
     rowIndex = 0;
      var svgParent = container.getElementsByTagName('svg')[0];
      Array.prototype.forEach.call(container.getElementsByTagName('rect'), function(bar) {
        var bounds;  // bounding box of text element

        // ignore rect if not a timeline bar
        if (parseFloat(bar.getAttribute('x')) > 0) {
          weekLabel = weekLabel.cloneNode(true);
          weekLabel.innerHTML = 'WW 70' + (rowIndex + 1);
          svgParent.appendChild(weekLabel);
          bounds = weekLabel.getBBox();
          weekLabel.setAttribute('x', parseFloat(bar.getAttribute('x')) + bounds.width);
          rowIndex++;
        }
      }); 
    });
	
	chart.draw(
		view,
		options, 
		dataTable, {
			tooltip: {
				isHtml: true,
			},
			timeline: {
				showBarLabels: false,
			}
	});
}

// hAxis put on top
    function afterDraw() {
	var g = document.getElementsByTagName("svg")[0].getElementsByTagName("g")[1];
	document.getElementsByTagName("svg")[0].parentNode.style.top = '40px';
	document.getElementsByTagName("svg")[0].style.overflow = 'visible';
	var height = Number(g.getElementsByTagName("text")[0].getAttribute('y')) + 15;
	g.setAttribute('transform','translate(0,-'+height+')');
	g = null;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
1
Hi @WhiteHat, that is the one you have provided me before and it is working correctly. But the problem is I want to put it on top, and that is where I am having a problem. Is it possible and could you please help me on this one? - bbb
Hi anyone who could advise me on this one? Thank you in advance! - bbb

1 Answers

0
votes

The error is in the // remove haxis labels part. The value in your filter is "Introduction to Programming" (i.e the last <text> content of the graph) but you're filtering for column 5, a date, which is what causes the error.

There's no need to try and go into the <svg> to change the labels though (and it's futile anyway because the labels will no longer match the bars).

Just use this:

hAxis: {
  minValue: new Date(2016, 9, 29),
  maxValue: new Date(2016, 10, 28),
},

google.charts.load("current", {
  packages: ["timeline"],
  callback: drawChart
});

function drawChart() {
  var container = document.getElementById('chart_div');
  var chart = new google.visualization.Timeline(container);

  // hAxis put on top
  google.visualization.events.addListener(chart, 'ready', afterDraw);

  // Link in tooltip
  google.visualization.events.addListener(chart, 'select', function(e) {
    var tooltip = document.querySelector('.google-visualization-tooltip:not([clone])');
    if (chart.ttclone) {
      chart.ttclone.parentNode.removeChild(chart.ttclone)
    }
    chart.ttclone = tooltip.cloneNode(true);
    chart.ttclone.setAttribute('clone', true);
    chart.ttclone.style.pointerEvents = 'auto';
    tooltip.parentNode.insertBefore(chart.ttclone, chart.tooltip);
  });

  var dataTable = new google.visualization.DataTable();
  dataTable.addColumn({
    type: 'string',
    id: 'Name'
  });
  // for colorMap
  dataTable.addColumn({
    type: 'string',
    id: 'Course'
  });
  dataTable.addColumn({
    type: 'string',
    id: 'Subject'
  });
  dataTable.addColumn({
    type: 'string',
    id: 'ToolTip',
    role: 'tooltip',
    p: {
      html: true
    }
  });
  dataTable.addColumn({
    type: 'date',
    id: 'Start'
  });
  dataTable.addColumn({
    type: 'date',
    id: 'End'
  });
  dataTable.addRows([

    // Timeline Start 
    ['Student 1', 'ENGR', 'Trigonometry', '<a href="link_to_subj_desc" target="_blank">Trigonometry</a>', new Date(2016, 9, 30), new Date(2016, 10, 06)],
    ['Student 2', 'IT', 'DB Management', '<a href="link_to_subj_desc" target="_blank">DB Management</a>', new Date(2016, 9, 30), new Date(2016, 10, 13)],
    ['Student 3', 'CS', 'Introduction to Programming', '<a href="link_to_subj_desc" target="_blank">Introduction to Programming</a>', new Date(2016, 9, 30), new Date(2016, 10, 27)]
  ]);

  var colors = [];
  var colorMap = {
    ENGR: '#2ECC71', // Green
    IT: '#E67E22', // Brown
    CS: '#9B59B6', // Violet
  };

  for (var i = 0; i < dataTable.getNumberOfRows(); i++) {
    colors.push(colorMap[dataTable.getValue(i, 1)]);
  }

  var rowHeight = 41;
  var chartHeight = (dataTable.getNumberOfRows() + 1) * rowHeight;

  var options = {
    timeline: {
      groupByRowLabel: true,
      rowLabelStyle: {
        fontName: 'Century Gothic',
        fontSize: 14,
        color: '#333333',
        bold: 'true',
      },
      barLabelStyle: {
        fontName: 'Century Gothic',
        fontSize: 11,
      },
      showRowLabels: true,
      showBarLabels: true,
    },
    hAxis: {
      minValue: new Date(2016, 9, 29),
      maxValue: new Date(2016, 10, 28),
    },
    avoidOverlappingGridLines: true,
    height: chartHeight,
    width: '100%',
    colors: colors,
  };



  // use a DataView to hide the category column from the Timeline
  var view = new google.visualization.DataView(dataTable);
  view.setColumns([0, 2, 3, 4, 5]);

  chart.draw(
    view,
    options,
    dataTable, {
      tooltip: {
        isHtml: true,
      },
      timeline: {
        showBarLabels: false,
      }
    });
}

// hAxis put on top
function afterDraw() {
  var g = document.getElementsByTagName("svg")[0].getElementsByTagName("g")[1];
  document.getElementsByTagName("svg")[0].parentNode.style.top = '40px';
  document.getElementsByTagName("svg")[0].style.overflow = 'visible';
  var height = Number(g.getElementsByTagName("text")[0].getAttribute('y')) + 15;
  g.setAttribute('transform', 'translate(0,-' + height + ')');
  g = null;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>