Creating a custom Chart.js legend style
Tags: javascript, frontend, tutorial, ui
Published: 2020-09-19 01:52:43

For those who don't know [chart.js](https://www.chartjs.org/), it's a `javascript` chart library.

Using a library for creating data visualization can be a little painful when you want something beyond the examples and styles provided by those libraries.
I've decided creating this post when I spent a lot of effort to customize a doughnut chart style, cause I needed to use a custom legend style for that chart.

This is what you can create without any custom styling:

![Chart.js example](https://dev-to-uploads.s3.amazonaws.com/i/p8pvo3zlic960io77arg.JPG)

So going deep into the [documentation](https://www.chartjs.org/docs/latest/configuration/legend.html#html-legends), there is a `legendCallback` option that enables us to insert a HTML legend to the chart and this will be rendered once we call `generateLegend()` function from chart.js.
This is what my legendCallback looks like:
```javascript
legendCallback: (chart) => {
        const renderLabels = (chart) => {
          const { data } = chart;
          return data.datasets[0].data
            .map(
              (_, i) =>
                `<li>
                    <div id="legend-${i}-item" class="legend-item">
                      <span style="background-color:
                        ${data.datasets[0].backgroundColor[i]}">
                        &nbsp;&nbsp;&nbsp;&nbsp;
                      </span>
                      ${
                        data.labels[i] &&
                        `<span class="label">${data.labels[i]}: $${data.datasets[0].data[i]}</span>`
                      }
                    </div>
                </li>
              `
            )
            .join("");
        };
        return `
          <ul class="chartjs-legend">
            ${renderLabels(chart)}
          </ul>`;
      },
```
Here I'm mapping through all elements in the dataset and getting it's background color and label (previously defined inside the charts options object).

With this HTML + some CSS I can generate something like this:

![Chart.js custom legend style](https://dev-to-uploads.s3.amazonaws.com/i/8k7suzbdcguuhcvjn4ql.JPG)

YES! JOB DONE!
... actually not quite ;) 

WHAT?

yup, until this point we have the legend style but if we click on it, nothing happens on the chart... we don't have that excluding data animation as if we were using the default legend.

![Chartjs legend gif](https://dev-to-uploads.s3.amazonaws.com/i/v07b0h82r1ucztgx715z.gif)

We need to create click event listeners for each legend:
```javascript
const legendItemSelector = ".legend-item";
  const labelSeletor = ".label";

  const legendItems = [
    ...containerElement.querySelectorAll(legendItemSelector)
  ];
  legendItems.forEach((item, i) => {
    item.addEventListener("click", (e) =>
      updateDataset(e.target.parentNode, i)
    );
  });
```
And then based on the current state of the data (available in this `getDatasetMeta` function) from the legend you clicked, you can hide and show that data in the chart:
```javascript
const updateDataset = (currentEl, index) => {
    const meta = myChart.getDatasetMeta(0);
    const labelEl = currentEl.querySelector(labelSeletor);
    const result = meta.data[index].hidden === true ? false : true;
    if (result === true) {
      meta.data[index].hidden = true;
      labelEl.style.textDecoration = "line-through";
    } else {
      labelEl.style.textDecoration = "none";
      meta.data[index].hidden = false;
    }
    myChart.update();
  };
```

Together they look like this:
```javascript
export const bindChartEvents = (myChart, containerElement) => {
  const legendItemSelector = ".legend-item";
  const labelSeletor = ".label";

  const legendItems = [
    ...containerElement.querySelectorAll(legendItemSelector)
  ];
  legendItems.forEach((item, i) => {
    item.addEventListener("click", (e) =>
      updateDataset(e.target.parentNode, i)
    );
  });

  const updateDataset = (currentEl, index) => {
    const meta = myChart.getDatasetMeta(0);
    const labelEl = currentEl.querySelector(labelSeletor);
    const result = meta.data[index].hidden === true ? false : true;
    if (result === true) {
      meta.data[index].hidden = true;
      labelEl.style.textDecoration = "line-through";
    } else {
      labelEl.style.textDecoration = "none";
      meta.data[index].hidden = false;
    }
    myChart.update();
  };
};

```
And now we are able to click and have those chart.js animations:

![Chartjs legend animation gif](https://dev-to-uploads.s3.amazonaws.com/i/ivo757m2y2o6ygcs0kwi.gif)

This post is more focused on the custom styling so if you are curious about how to create a chart.js chart and make that work, here is the example that you can take a look 😄
{% codesandbox oh3kc %}
[![Edit chartjs-legend-styling](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/chartjs-legend-styling-oh3kc?fontsize=14&hidenavigation=1&theme=dark)

See you in the next post! Hope you enjoy it.