Chart Legend in an External Container

This tutorial will show you how you can easily move your chart legend outside main container into a totally separate one.

NOTE We're going to be using Pie chart as an example in this tutorial. However, this applies to any other chart type that uses Legend.

Base chart

Let's start with a basic Pie chart with a Legend:

See the Pen amCharts 4: Legend in external div (1) by amCharts (@amcharts) on CodePen.24419

We've done little else, just created a Pie chart with a single series, then added a Legend.

As you can see the legend takes up the majority of chart area, making our Pie very small.

Creating a separate container for Legend

We could increase the height of the chart container. That's one way to go about it.

Another is to move the legend to its own container, that is completely independent from the main chart. As a consequence, it can also be styled and sized, independently.

Let's go ahead and create a separate div for the legend, next to the one we have for the chart:

<style>
#chartdiv, #legenddiv {
  width: 100%;
  height: 200px;
  border: 1px dotted #c99;
  margin: 1em 0;
}

#legenddiv {
  height: 150px;
}
</style>

Chart:
<div id="chartdiv"></div>

Legend:
<div id="legenddiv"></div>

That will create a separate <div>. Now, all we have to do is to create a new Container instance in it.

We create a new container just like we create a chart, using am4core.create() function. (or am4core.createFromConfig() if we are using JSON-based config)

let legendContainer = am4core.create("legenddiv", am4core.Container);
legendContainer.width = am4core.percent(100);
legendContainer.height = am4core.percent(100);
var legendContainer = am4core.create("legenddiv", am4core.Container);
legendContainer.width = am4core.percent(100);
legendContainer.height = am4core.percent(100);
var legendContainer = am4core.createFromConfig({
  "width": "100%",
  "height": "100%"
}, "legenddiv", am4core.Container);

IMPORTANT Plain Container, unlike charts, does not set its own dimensions automatically. We need to set them manually. In the above example with set them to take up the whole area of our "legenddiv".

Moving the Legend

Now, all we have left to do is to move our Legend to the new container.

This is super easy, just set Legend's parent to a reference to our Legend container:

chart.legend.parent = legendContainer;
chart.legend.parent = legendContainer;
var legendContainer = am4core.createFromConfig({
  "width": "100%",
  "height": "100%"
}, "legenddiv", am4core.Container);

var chart = am4core.createFromConfig({
  // ...
  "legend": {
    "parent": legendContainer
  }
}, "chartdiv", am4charts.PieChart);

Let's see if it worked:

See the Pen amCharts 4: Legend in external div (2) by amCharts (@amcharts) on CodePen.24419

Yup, it did!

Sizing the legend

So far we had the <div> that houses legend set at a fixed height. This means that, depending on a number of legend items, and available width, some items might not fit, or there might be some white space left.

Let's look at some ways to fix that.

Dynamically resizing legend div

Or task here is simple - once chart initializes, we want to size the legend <div> to fit actual height of the legend perfectly.

For that we will be using two chart events:

  • datavalidated - for when chart initially loads data, or for events if data was changed dynamically;
  • maxsizechanged - for when the chart resizes, e.g. when window is resized and our legend arrangement might have changed.

Those events will trigger a function, that will check legend's special property contentHeight which returns actual height of all the items in legend, including those items that do not fit, and will dynamically update legend's <div>'s style.height.

Let's whip it all up together:

chart.events.on("datavalidated", resizeLegend);
chart.events.on("maxsizechanged", resizeLegend);

function resizeLegend(ev) {
  document.getElementById("legenddiv").style.height = chart.legend.contentHeight + "px";
}
chart.events.on("datavalidated", resizeLegend);
chart.events.on("maxsizechanged", resizeLegend);

function resizeLegend(ev) {
  document.getElementById("legenddiv").style.height = chart.legend.contentHeight + "px";
}
var chart = am4core.createFromConfig({
  // ...
  "events": {
    "datavalidated": resizeLegend,
    "maxsizechanged": resizeLegend
  }
}, "chartdiv", am4charts.PieChart);

function resizeLegend(ev) {
  document.getElementById("legenddiv").style.height = chart.legend.contentHeight + "px";
}

See the Pen amCharts 4: Legend in external div (3) by amCharts (@amcharts) on CodePen.24419

Now, you should see the legend resize to fit its contents perfectly, even if you resize the demo window.

Making legend scrollable

Resizing the legend is fine. However, we might not want to grow past some point if we have a lot of items.

Let's try and make our legend scrollable instead.

The natural instinct would be to plop something like this CSS on legend's <div> and call it a day:

#legenddiv {
  max-height: 150px;
  overflow: auto;
}

That, unfortunately, won't work.

The Container element, that we "outsource" our Legend to, will always try to size itself based on the parent <div> which will result in legend always being capped at 150 pixels height, and thus not overflow directive not kicking in.

What we need to do is to wrap our legend <div> into another div (let's give it an id of "legendwrapper"), that has the above CSS.

The inner element will always size itself as we set it up to in previous section, while outer element (wrapper) will keep it constrained to certain height.

Let's try it.

<style>
#chartdiv, #legendwrapper {
  width: 100%;
  height: 200px;
  border: 1px dotted #c99;
  margin: 1em 0;
}

#legenddiv {
  height: 150px;
}

#legendwrapper {
  max-height: 120px;
  overflow-x: none;
  overflow-y: auto;
}
</style>

Chart:
<div id="chartdiv"></div>

Legend:
<div id="legendwrapper">
  <div id="legenddiv"></div>
</div>

Let's see if that worked:

See the Pen amCharts 4: Legend in external div (4) by amCharts (@amcharts) on CodePen.24419