{"id":20062,"date":"2020-07-09T10:38:46","date_gmt":"2020-07-09T09:38:46","guid":{"rendered":"http:\/\/www.highcharts.com\/blog\/?p=20062"},"modified":"2026-01-13T09:54:20","modified_gmt":"2026-01-13T09:54:20","slug":"bar-chart-race","status":"publish","type":"post","link":"https:\/\/www.highcharts.com\/blog\/tutorials\/bar-chart-race\/","title":{"rendered":"Bar chart race"},"content":{"rendered":"<p>Have you heard of a Bar Chart Race? No? Well, it\u2019s not a board game for data scientists, but, actually, a useful way to display time series in a bar chart format via animation.<\/p>\n<p>Here is an example, and below we\u2019ll show how to create this chart.<\/p>\n<p class=\"demo-container\"><iframe height=\"990\" style=\"width: 100%;\" scrolling=\"no\" src=\"https:\/\/codepen.io\/mushigh\/embed\/mdWrKrZ?height=265&#038;theme-id=light&#038;default-tab=result\" frameborder=\"no\" loading=\"lazy\" allowtransparency=\"true\" allowfullscreen=\"true\" title=\"A bar race chart displays the world population from 1960 to 2018\"><br \/>\n  See the Pen <a href='https:\/\/codepen.io\/mushigh\/pen\/mdWrKrZ'>Bar race<\/a> by mustapha mekhatria<br \/>\n  (<a href='https:\/\/codepen.io\/mushigh'>@mushigh<\/a>) on <a href='https:\/\/codepen.io'>CodePen<\/a>.<br \/>\n<\/iframe><\/p>\n<p>Creating a bar chart race with Highcharts library is easy and straightforward, thanks to the <a href=\"https:\/\/api.highcharts.com\/highcharts\/series.bar.dataSorting\" target=\"_blank\" rel=\"noopener noreferrer\">dataSorting<\/a> feature. And in this tutorial, we will show you how to create a world population bar chart race.<\/p>\n<p>Let\u2019s get started!<\/p>\n<p>The data used in this tutorial is the world population from 1960 to 2018. Here is the link to the data used in this <a href=\"https:\/\/api.npoint.io\/d70fd8f47a70326609bb\" target=\"_blank\" rel=\"noopener noreferrer\">demo<\/a>. Now, we have the data; let\u2019s make a function that processes the data for a particular year.<\/p>\n<pre>\/**\r\n * Calculate the data output\r\n *\/\r\nfunction getData(year) {\r\n\r\n  let output = initialData.map(data =&gt; {\r\n    return [data[\"Country Name\"], data[year]]\r\n  }).sort((a, b) =&gt; b[1] - a[1]);\r\n\r\n  return ([output[0], output.slice(1, 11)]);\r\n}<\/pre>\n<p>The first result is in this demo that shows the data related to the year 1960:<\/p>\n<p class=\"demo-container\"><iframe height=\"500\" style=\"width: 100%;\" scrolling=\"no\" src=\"https:\/\/codepen.io\/mushigh\/embed\/MWpjXEE?height=265&#038;theme-id=light&#038;default-tab=result\" frameborder=\"no\" loading=\"lazy\" allowtransparency=\"true\" allowfullscreen=\"true\" title=\"A bar chart displays the world population in 1960\"><br \/>\n  See the Pen <a href='https:\/\/codepen.io\/mushigh\/pen\/MWpjXEE'>Bar race (no animation)<\/a> by mustapha mekhatria<br \/>\n  (<a href='https:\/\/codepen.io\/mushigh'>@mushigh<\/a>) on <a href='https:\/\/codepen.io'>CodePen<\/a>.<br \/>\n<\/iframe><\/p>\n<p>The next step is to add animation to the chart. To achieve this we need to add the following HTML elements: play\/stop button and the <code>&lt;input&gt;<\/code> element with <code>type=\u201drange\u201d<\/code> for the interactive progress-bar. We have also to add the styling effect! (see CSS below):<\/p>\n<pre>@import \"https:\/\/maxcdn.bootstrapcdn.com\/font-awesome\/4.3.0\/css\/font-awesome.min.css\";\r\n\r\n#parentContainer {\r\n  min-width: 400px;\r\n  max-width: 800px;\r\n}\r\n\r\n#play-controls {\r\n  position: absolute;\r\n  left: 100px;\r\n  top: 350px;\r\n}\r\n\r\n#play-pause-button {\r\n  width: 30px;\r\n  height: 30px;\r\n  cursor: pointer;\r\n  border: 1px solid silver;\r\n  border-radius: 3px;\r\n  background: #f8f8f8;\r\n}\r\n\r\n#play-range {\r\n  transform: translateY(2.5px);\r\n}<\/pre>\n<p>We will add this the function to fit the range width after the window resize:<\/p>\n<pre> events: {\r\n   render() {\r\n     let chart = this;\r\n\r\n     \/\/ Responsive input\r\n     input.style.width = chart.plotWidth - chart.legend.legendWidth + 'px'\r\n   }\r\n },<\/pre>\n<p>The result of the previous changes are in this demo:<\/p>\n<p class=\"demo-container\"><iframe height=\"500\" style=\"width: 100%;\" scrolling=\"no\" src=\"https:\/\/codepen.io\/mushigh\/embed\/ZEepRrr?height=265&#038;theme-id=light&#038;default-tab=result\" frameborder=\"no\" loading=\"lazy\" allowtransparency=\"true\" allowfullscreen=\"true\" title=\"A bar chart displays the world population in 1960\"><br \/>\n  See the Pen <a href='https:\/\/codepen.io\/mushigh\/pen\/ZEepRrr'>Bar race (with minimum animation)<\/a> by mustapha mekhatria<br \/>\n  (<a href='https:\/\/codepen.io\/mushigh'>@mushigh<\/a>) on <a href='https:\/\/codepen.io'>CodePen<\/a>.<br \/>\n<\/iframe><\/p>\n<p>So far, we have a button and a range bar element, let\u2019s create the function of the button to update the chart using the <code>series.update<\/code> feature:<\/p>\n<pre>\/**\r\n * Update the chart. This happens either on updating (moving) the range input,\r\n * or from a timer when the timeline is playing.\r\n *\/\r\nfunction update(increment) {\r\n  if (increment) {\r\n    input.value = parseInt(input.value) + increment;\r\n  }\r\n  if (input.value &gt;= endYear) { \/\/ Auto-pause\r\n    pause(btn);\r\n  }\r\n\r\n  chart.update({\r\n    title: {\r\n      useHTML: true,\r\n      text: `&lt;div&gt;World population - overall: &lt;b&gt;${getData(input.value)[0][1]}&lt;b&gt;&lt;\/span&gt;&lt;\/div&gt;`\r\n    },\r\n  }, false, false, false)\r\n\r\n  chart.series[0].update({\r\n    name: input.value,\r\n    data: getData(input.value)[1]\r\n  })\r\n}<\/pre>\n<p>And here, we link the function above to the button elements:<\/p>\n<pre>\/**\r\n * Play the timeline.\r\n *\/\r\nfunction play(button) {\r\n  button.title = 'pause';\r\n  button.className = 'fa fa-pause';\r\n  chart.sequenceTimer = setInterval(function() {\r\n    update(1);\r\n  }, 500);\r\n}\r\n\r\n\/** \r\n * Pause the timeline, either when the range is ended, or when clicking the pause button.\r\n * Pausing stops the timer and resets the button to play mode.\r\n *\/\r\nfunction pause(button) {\r\n  button.title = 'play';\r\n  button.className = 'fa fa-play';\r\n  clearTimeout(chart.sequenceTimer);\r\n  chart.sequenceTimer = undefined;\r\n}\r\n\r\nbtn.addEventListener('click', function() {\r\n  if (chart.sequenceTimer) {\r\n    pause(this)\r\n  } else {\r\n    play(this)\r\n  }\r\n})\r\n\r\n\/** \r\n * Trigger the update on the range bar click.\r\n *\/\r\ninput.addEventListener('click', function() {\r\n  update()\r\n})<\/pre>\n<p>Now, we have a fully worked race bar chart:<\/p>\n<p class=\"demo-container\"><iframe height=\"500\" style=\"width: 100%;\" scrolling=\"no\" src=\"https:\/\/codepen.io\/mushigh\/embed\/eYvdKaL?height=265&#038;theme-id=light&#038;default-tab=result\" frameborder=\"no\" loading=\"lazy\" allowtransparency=\"true\" allowfullscreen=\"true\" title=\"A bar race chart displays the world population from 1960 to 2018. By Sebastian Wedzel and G\u00f8ran Slettmarkan\"><br \/>\n  See the Pen <a href='https:\/\/codepen.io\/mushigh\/pen\/eYvdKaL'>Bar race: credit Sebastian Wedzel and G\u00f8ran Slettemarkan<\/a> by mustapha mekhatria<br \/>\n  (<a href='https:\/\/codepen.io\/mushigh'>@mushigh<\/a>) on <a href='https:\/\/codepen.io'>CodePen<\/a>.<br \/>\n<\/iframe><\/p>\n<p>As a final step, we can attach a custom functionality to tweak the data-labels changing effect:<\/p>\n<pre>\/**\r\n * Animate dataLabels functionality\r\n *\/\r\n(function(H) {\r\n  const FLOAT = \/^-?\\d+\\.?\\d*$\/;\r\n  \/\/ Add animated textSetter, just like fill\/strokeSetters\r\n  H.Fx.prototype.textSetter = function(proceed) {\r\n    var startValue = this.start.replace(\/ \/g, ''),\r\n      endValue = this.end.replace(\/ \/g, ''),\r\n      currentValue = this.end.replace(\/ \/g, '');\r\n    if ((startValue || '').match(FLOAT)) {\r\n      startValue = parseInt(startValue, 10);\r\n      endValue = parseInt(endValue, 10);\r\n\r\n      \/\/ No support for float\r\n      currentValue = Highcharts.numberFormat(\r\n        Math.round(startValue + (endValue - startValue) * this.pos), 0);\r\n    }\r\n    this.elem.endText = this.end;\r\n    this.elem.attr(\r\n      this.prop,\r\n      currentValue,\r\n      null,\r\n      true\r\n    );\r\n  };\r\n\r\n  \/\/ Add textGetter, not supported at all at this moment:\r\n  H.SVGElement.prototype.textGetter = function(hash, elem) {\r\n    var ct = this.text.element.textContent || '';\r\n    return this.endText ? this.endText : ct.substring(0, ct.length \/ 2);\r\n  }\r\n\r\n  \/\/ Temporary change label.attr() with label.animate():\r\n  \/\/ In core it's simple change attr(...) =&gt; animate(...) for text prop\r\n  H.wrap(H.Series.prototype, 'drawDataLabels', function(proceed) {\r\n    var ret,\r\n      attr = H.SVGElement.prototype.attr,\r\n      chart = this.chart;\r\n\r\n    if (chart.sequenceTimer) {\r\n      this.points.forEach(\r\n        point =&gt; (point.dataLabels || []).forEach(\r\n          label =&gt; label.attr = function(hash, val) {\r\n            if (hash &amp;&amp; hash.text !== undefined) {\r\n              var text = hash.text;\r\n              delete hash.text;\r\n              this.attr(hash);\r\n              this.animate({\r\n                text: text\r\n              });\r\n              return this;\r\n            } else {\r\n              return attr.apply(this, arguments);\r\n            }\r\n          }\r\n        )\r\n      );\r\n    }\r\n    ret = proceed.apply(this, Array.prototype.slice.call(arguments, 1));\r\n    this.points.forEach(\r\n      p =&gt; (p.dataLabels || []).forEach(d =&gt; d.attr = attr)\r\n    );\r\n    return ret;\r\n  });\r\n})(Highcharts);<\/pre>\n<p>The final result is in the demo below:<\/p>\n<p class=\"demo-container\"><iframe height=\"990\" style=\"width: 100%;\" scrolling=\"no\"  src=\"https:\/\/codepen.io\/mushigh\/embed\/mdWrKrZ?height=265&#038;theme-id=light&#038;default-tab=result\" frameborder=\"no\" loading=\"lazy\" allowtransparency=\"true\" allowfullscreen=\"true\" title=\"A bar race chart displays the world population from 1960 to 2018\"><br \/>\n  See the Pen <a href='https:\/\/codepen.io\/mushigh\/pen\/mdWrKrZ'>Bar race<\/a> by mustapha mekhatria<br \/>\n  (<a href='https:\/\/codepen.io\/mushigh'>@mushigh<\/a>) on <a href='https:\/\/codepen.io'>CodePen<\/a>.<br \/>\n<\/iframe><\/p>\n<p>As we promised, it is easy and straightforward to create a bar chart race with Highcharts.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Learn how to create an interactive bar chart race with Highcharts.<\/p>\n","protected":false},"author":254,"featured_media":20082,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"meta_title":"","meta_description":"","hc_selected_options":[],"footnotes":""},"categories":[210],"tags":[1094],"coauthors":[835],"class_list":["post-20062","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials","tag-highcharts-core"],"_links":{"self":[{"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/posts\/20062","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/users\/254"}],"replies":[{"embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/comments?post=20062"}],"version-history":[{"count":1,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/posts\/20062\/revisions"}],"predecessor-version":[{"id":29307,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/posts\/20062\/revisions\/29307"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/media\/20082"}],"wp:attachment":[{"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/media?parent=20062"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/categories?post=20062"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/tags?post=20062"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.highcharts.com\/blog\/wp-json\/wp\/v2\/coauthors?post=20062"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}