Skip to content

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.

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

Open in CodePen

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.

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);

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;

Let’s see if it worked:

Open in CodePen

Yup, it did!

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.

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 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.

Since data can be updated in a number of ways, and legend is in different <div>, we’re going to be adding those events to both chart and the legend itself.

Let’s whip it all up together:

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

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

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.

There are two ways to make our legend scrollable: one which involves some HTML and CSS trickery; the other using built-in amCharts features.

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:

Open in CodePen

Another, probably easier, option is to use Legend’s scrollable option.

Basically, setting this option to true will fix the legend height at certain number of pixels. If it fits it fits. If it doesn’t a built-in scrollbar will appear.

chart.legend.scrollable = true;

Using this method we don’t have to care about sizing the legend container dynamically.

Open in CodePen

Since 4.2.0 it’s possible to include extra elements when exporting chart to images. That means we can include our external legend as well:

chart.exporting.menu = new am4core.ExportMenu();
chart.exporting.extraSprites.push({
"sprite": legendContainer,
"position": "bottom",
"marginTop": 20
});

Try it out live:

Open in CodePen

That means calling dispose() on both chart and the Container instance that holds your legend.

chart.legend.dispose();