Chart.js: [BUG] Chart invisible when Legend too large

Created on 3 Jan 2017  路  22Comments  路  Source: chartjs/Chart.js

Expected Behavior



Imagine a Pie/Doughnut Chart with the following requirements:

  1. It has a Dataset of 100 Products
  2. Each Product is represented by a Legend Label (thus, 100 labels)
  3. The Chart resides within a medium div (say "col-lg-4" of Bootstrap)

It can be obvious that both the 6 generated columns with the Labels cannot fit to the div with the Chart. In my opinion, they should not.

Image presenting the Chart in a full-screen mode:
largechart

Current Behavior



When working with a Chart following the aforementioned requirements:

  1. The div of the chart (canvas) shows some of the Legend Labels (say the 60 last ones)
  2. The other Labels are hidden, rendered outside the canvas
  3. The Chart is nowhere to be seen, probably thrown out by the Labels

Image presenting the Chart in a smaller Div. Most elements are gone. All that is left are some of the Legend Labels:
smallchart

Possible Solution



Add a scrollable overflow to the "box" that hosts the Legends?

Steps to Reproduce


  1. Create a medium div (any size will do, as long as it's not a full-screen one)
    <div class="row">
    <div class="col-lg-4">
    <div>
    <div>
  2. In the "col-lg-4" div, place a canvas for your Chart. Make it :
  3. Type = Pie/Doughnut
  4. Responsive = true
  5. Fullwidth = true (although "false" has the exact same behavior)
  6. Dataset = any array holding 100+ elements (e.g. array of {name, value})
  7. Labels = the "name" values of your array (so that you will have 100 labels)
    Options Example:
    options="{ 'responsive': true,'maintainAspectRatio':true,'transparencyEffects':true,'dataSetBorderWidth': 2, 'legend':{ 'display': true, 'fullWidth':true, 'position': 'right', 'labels':{'boxWidth':40, 'fontSize':12, 'fontStyle':'normal', 'fontColor':'#666', 'usePointStyle':false}}, 'pallette': ['#a694ff','#a59891','#98a7ad','#ea7d7d','#80d27a','#99b4f4','#fed444','#3f3f3f','#b6696c','#417d83','#ff9750']}"

When you run it, you will see that your div will only present some of the Legend Labels. All other Labels and the Chart will be cone.

Environment

  • Chart.js version:2.2.1
  • Browser name and version: Chrome & Mozilla
enhancement

Most helpful comment

I have an idea. What if Chart.js' constructor took in 2 canvases, 1 for the Chart itself, and the other for the Legend?

That way, one could control the sizing and positioning of each canvas separately.

For example, to solve the above problem, one could simply increase the height of the Legend canvas and wrap it with a scrollable <div> (with overflow: scroll;), without affecting the Chart canvas.

This would also solve issue #1959 (size of Chart will not change if the height/width of the Legend changes).

All 22 comments

Hmmm, I'm not sure what the best solution is here. I almost think an HTML legend would be the way to go in this case.

@chartjs/maintainers any thoughts?

@etimberg I agree that an HTML legend would be the best approach, and it covers that "scrollable overflow." I don't think that most users will have this many legend options, so an enhancement to the core library doesn't seem necessary.

I agree as well and that is something I was trying to accomplish, namely:

  • Hide the Legend from the Original chart
  • Use the legendCallback to display the Legend in another div
  • Copy all the properties and events of the original Legend into the new one, so as to avoid code duplication

However, no matter how much I tried, I could not copy the styles and events from the Chart component into my generated legend. I saw that a previous version had a legendTemplate as well, that could maybe solve my problem (regarding the code duplication), but it is not available any more.

Maybe if the legendCallback could return a list of properties with their styles and events, rather than a plain HTML list? (Which was what I was trying to do and maybe would accomplish if I had more extensive Javascript and HTML Canvas knowledge..). Would that be hard or undoable?

It would be almost impossible to directly copy over the styles from the canvas since they aren't in CSS. It is possible to update the default legendCallback to produce something that look much more like canvas one. That shouldn't actually be that hard since it would be in the string generation. To reduce code duplication, we could move the generateLegend implementation into the actual chart legend. That way, the legend could run all the normal stuff to generate labels, click handling, etc, then all we have to do is bind it all together and return something that gets put into the DOM tree (would probably need to return the <div> at the top of the tree rather than a string to attach functions correctly. This would be a major change though and I would not want to break any existing HTML legends.

The legendTemplate was used in v1 to generate the legend but it was just a shorthand for building the legend HTML string so it didn't end up getting much use.

You could change the legendCallback in your chart to return something else. Then, when you call chart.generateLegend() you will get whatever is returned from legendCallback.

I like the html idea @zachpanz88, but this is a real issue.
there are a couple of problems which this 'layout bug' can interfere.

i use this lib on a real application, with angular-chart.
screen shot 2017-01-18 at 19 06 05

  1. as the legends grow, the graphs become smaller, this is merely aesthetic, but each graph depending on the size of the legend is a mess. especially when they are on the same row. gives the impression of a poor design/alignment. luckly, the last chart didnt had a 4th legend, but if it had we were going to see 3 different pie chart sizes on that row. ( _eww!_ 馃槚 )

  2. @Queenferno mentioned a 100items list.. but on my CRM app, i dont have this many items, still i get the same problem. as in the image, i have a row, with 3 charts. Campaigns, Responsibles, and Deal Status. The campaign's legend array, have just a couple of.. less than 10 items. And the graph is already gone. 馃槥

@itsmelion what should be the expected layout/design of these charts at this size (let's say the last one "Status" have an extra legend item)?

@itsmelion Anyway, this chart is created dynamically. If the user chooses "All time chart" in the filters, he could have 1k Campaigns, and it will generate 1k legend's items.

Maybe the best solution for this is what @Queenferno proposed, if we could take the _legendCallback_ properties for styles and events to interact with the chart.

I think that is important to have an option for this in the bundle of chart.js, not as outside solution. If we have a "wrap" option for the text in the legend and "max-width", the text will be automatically ajust to the width.

I have an idea. What if Chart.js' constructor took in 2 canvases, 1 for the Chart itself, and the other for the Legend?

That way, one could control the sizing and positioning of each canvas separately.

For example, to solve the above problem, one could simply increase the height of the Legend canvas and wrap it with a scrollable <div> (with overflow: scroll;), without affecting the Chart canvas.

This would also solve issue #1959 (size of Chart will not change if the height/width of the Legend changes).

Did anyone find a solution to this as I am having the exact problem.

Thanks

I'm not expert but we could be possible to show only top 5 legends to avoid an invisible chart

@bahiamartins yes, this can be done. A custom label generation function that returns the first 5 will work

Using a legendCallback function works to create the html legend, but does anyone have a working example that also handles clicks on legend items with the same behavior as the default legend?

I am experiencing the same problem. Any good solution for this that includes the legend's events?

@renielDev Here is the solution I wrote:
Fiddle

But fair warning
This is a hacky solution, that probably won't work if you have multiple datasets. I have stripped the fiddle down to the minimum, but it's taken from production code, so there's probably still some "noise" in it. If anything is unclear, let me know and I will try my best to explain the madness.

Will look into it @timbauwens. Thanks

The solution of @timbauwens looks nice, but as he mentioned it is a bit hacky (still much appreciated). Still, a more "official" solution would be nice. Like if the core could be altered in the way that @daniel-shuy proposed.

Any updates on a solution to this issue? Custom responsive legends, (including piping in information for localization and canvas size), get very messy very quickly. These types of coding gymnastics are not generally necessary with most charting libraries.

I believe solution described by @PabloMCampos would be the easiest to use, although not necessarily the easiest to develop.

I am also having the issue, but it is much more evident for mobile devices. Any solution to this?

Didn't think that this would be an issue for me, till my data set enlarged! The chart becomes a tiny spec on the canvas while the legend takes center stage, which beats the point of having a chart! @daniel-shuy solutions seems practical.

How is this issue still opened? Is there no way to add a 'min-height' to the graph so it doesn't shrink? That way the container will grow.

How are you guys handling this problem?
There is toBase64Image method available, but maintainers recommend to use HTML legend instead. This doesn't make any sense.

  • If i want to save chart with title, data and legend i can't really do it, because chart looks tiny for large datasets.
  • If i use HTML legends then this image will not contain that

Is there any chance to modify canvas height by height of legend for horizontal layout?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

akashrajkn picture akashrajkn  路  3Comments

Woogles picture Woogles  路  3Comments

nanospeck picture nanospeck  路  3Comments

gabrieldesouza picture gabrieldesouza  路  3Comments

JewelsJLF picture JewelsJLF  路  3Comments