Data Visualization: How to Overcome Highcharts.js Drawbacks
Azoft Blog Data Visualization: How to Overcome Highcharts.js Drawbacks

Data Visualization: How to Overcome Highcharts.js Drawbacks

By Anton Kavytin on July 17, 2013

Data Visualization: Fixing Highcharts.js Drawbacks

During my recent web projects I happened to work with Highcharts.js — a great library for creating good-looking charts for websites and web applications. While it has many obvious advantages, there are also a number of disadvantages: buggy rendering of multi-line titles, descriptions, and axis labels, and absence of support for custom graphics and text in the generated charts. How to overcome these difficulties — in todays article.

Highcharts.js pros

Let me start with the benefits. First and foremost, this library is fairly easy to use: just include highcharts.js and you're ready. In addition, Highcharts.js offers:

  • Lots of customization options for rendered charts and labels;
  • Numerous chart types and the ability to combine them in one document;
  • Cross-browser compatibility (SVG is used for rendering);
  • Fairly comprehensive documentation with many examples;
  • Availability of charts in any of the following formats: pdf, png, jpg, svg.

Cons

  • Buggy rendering of multi-line titles and descriptions (text overlaps the graphics).
  • Axis labels may overlap if long enough.
  • No support for custom graphics and text in the generated charts, e.g. some additional text to the left of a chart. It is possible to implement this functionality by adding a bit of extra code to the framework.

Solutions

1. Multi-line titles and descriptions

The straightforward solution to this problem is to add an extra margin to the chart equal to the sum of header and description heights. It's extremely easy to add the margin:

var title = ‘Really long chart title’,
titleFontSize = 12, // set to your liking
subtitle = ‘Really long chart description’,
subtitleFontSize = 12, // set to your liking
  	 chart = new Highcharts.Chart({
chart: {
  			 // chart options go here
  	 },
  	 title: {
  		 text: title,
  		 style: {
  			 fontSize: titleFontSize + 'px'
  		 },
  	 margin: setTitleMargin(title, 0, 45, titleFontSize) + setSubtitleMargin(subtitle, -50, subtitleFontSize)
  	 },  	 
  	 subtitle: {
  		 text: subtitle,
  		 style: {
  			 fontSize: subtitleFontSize + 'px'
  		 },
  		 y: setTitleMargin(title, -10, titleFontSize)
  	 });

, where

/*
str — the string with title,
delta — additional margin between the title and the content,
charNum — number of characters per line,
fontSize — font size
*/

function setTitleMargin(str, delta, charNum, fontSize) {
  var size = fontSize || 20;
  return parseInt(str.length/ charNum, 10) * size + 20 + (delta ? delta : 0);
}

function setSubtitleMargin(str, delta, charNum, fontSize) {
  if( str.length === 0 ) {
  	 return 0;
  }
  var size = fontSize || 16;
  return parseInt(str.length/ charNum, 10) * size + 16 + (delta ? delta : 0);
}

The above code sets the title margin to a value equal to the number of lines in the chart title multiplied by the line height plus the number of lines in the chart description multiplied by the line height. The chart description should have its top margin extended by a value equal to the number of lines in the description multiplied by the line height.

2. Additional text in the generated charts

There's an awesome online example of custom chart retrieval using the highcharts.com server. It is implemented in the functions Highcharts.getSVG and Highcharts.exportCharts. To add the additional text to the left or to the right of the chart rewrite the Highcharts.getSVG function as follows:

Highcharts.getSVG = function(charts) {
  	 var svgArr = [], top = 0, width = 0;
  	 var lines = '';
  	 $.each(charts, function(i, chart) {  
  		 chart.options.chart.backgroundColor = {
  			 linearGradient: [0, 0, 0, 300],
  			 stops: [
  				 [0, '#FFFFFF'],
  				 [1, '#E0E0E0']
  			 ]
  		 };
  			 
  		 chart.options.chart.plotBackgroundColor = {
  			 linearGradient: [0, 0, 0, 300],
  			 stops: [
  				 [0, '#FFFFFF'],
  				 [1, '#E0E0E0']
  			 ]
  		 };
  	 
  		 var svg = chart.getSVG();
  		 svg = svg.replace('<svg', '<g transform="translate(0,' + top + ')" ');
  		 top += parseInt(chart.chartHeight) + 20;
  		 svg = svg.replace('</svg>', '</g>');
  		 lines += '<path stroke-width="1" stroke="#C0C0C0" d="M 0 ' + top + ' L 380 ' + top + '" fill="none"></path>';
  		 width = Math.max(width, chart.chartWidth);
  		 svgArr.push(svg);
  	 });
  	 width = 828; // exported file width
  	 top = parseInt($(document).height()); // find out the size of the exported document
  	 var topMarg = 40; // initial top margin
  	 var texts = '';
  	 var paragraphes = $(document).find('p'); // get all the paragraphs to add to the exported document
  	 for( var i = 0, l = paragraphes.length; i < l; i++ ) {
  		 texts += '<g>' + setTSpan($(paragraphes[i]).html(), topMarg, 18) + '</g>'; // render text in SVG
  		 topMarg += getTSpanHeight($(paragraphes[i]).html(), 1, 18) + 14; // calculate the margin for the next paragraph
  	 }
  	 top += 20;
  	 return '<svg height="'+ top +'" width="' + width + '" version="1.1" xmlns="http://www.w3.org/2000/svg"><g><g>' + texts + '</g><g transform="translate(‘ + width/2 + ’, 20)">' + svgArr.join('') + '<g>' + lines + '</g></g></g></svg>';
}
/*
str — text to display,
start — initial rendering position,
charNum — number of characters per line,
fontSize — font size,
number — total number of lines in the text
*/
function setTSpan(str, start, charNum, fontSize) {
  if( str.length > charNum ) {
  	 var index = charNum,
new_str = str.substring(0, index);
  	 if( str[56] !== ' ' ) {
  		 index = new_str.lastIndexOf(' ');
  		 new_str = new_str.substring(0, index);
  	 }
  	 return '<text x="65" y="' + start +'" text-anchor="start" text-align="justify">' + new_str + '</text>' + setTSpan(str.substring(index), start + fontSize);
  }
  return '<text x="65" y="' + start + '">' + str + '</text>';
}

function getTSpanHeight(str, number, charNum, fontSize) {
  if( str.length > charNum) {
  	 var index = charNum;
  	 var new_str = str.substring(0, index);
  	 if( str[charNum + 1] !== ' ' ) {
  		 index = new_str.lastIndexOf(' ');
  		 new_str = new_str.substring(0, index);
  	 }
  	 return fontSize + getTSpanHeight(str.substring(index), number+1, charNum, fontSize);
  }
  return fontSize;
 }

In conclusion

Highcharts.js is the first thing that comes to my mind when I stumble upon charts in my front-end projects (if you’re looking for a data visualization tool for server-side projects, you can try JpGraph). Highcharts.js is pleasant to work with and even though it has a few issues, they can be easily fixed, as you just learned.

VN:F [1.9.22_1171]
Rating: 5.0/5 (7 votes cast)
VN:F [1.9.22_1171]
Rating: +5 (from 5 votes)
Data Visualization: How to Overcome Highcharts.js Drawbacks, 5.0 out of 5 based on 7 ratings



Request a Free Quote
 
 
 

Please enter the result and submit the form

Content created by Anton Kavytin