D3.js Get Legend Outside the Chart

I am trying to get a legend about my chart outside the chart area.

Here are the fields:

var margin = {top: 50, right: 200, bottom: 50, left: 40}; var width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; 

First I create svg:

 var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 

So, from my point of view, now I have an svg canvas element and inside it that contains a diagram. I'm trying to add more to the right edge, so I can get some space between the svg canvas and g added to it, which contains the chart. Then I want to put my legend in this empty place.

This is where I add my legend:

 //add legend var legend = svg.append("g") .attr("class", "legend") .attr("height", 300) .attr("width", 200) .attr("transform", "translate(-1000,50"); 

Even if I add an SVG element, it is added to g in the svg element. Therefore, no matter how much I translated it or try to make it move further on the screen, it never passes the width of the inner g.

When troubleshooting, I see that the external SVG element has a height of 960 and a width of 500. The g attached to it has a conversion / translation of 40.50. The width ends at 839px at 433.223px (not sure if I understand that). External svg has tons of space right now due to the built-in field.

So, I'm trying to either increase the width g attached to svg to put my legend as a child of g and move it to the empty space created around the edge. Or, I'm trying to create another g, which is a brother for the first g, and then I can use the empty space created around the edge.

I can neither work nor know which way is better.

+5
source share
2 answers

Note that var svg assigned to <g> nested inside <svg>

 svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") // <-- This is what svg is currently being assigned to .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 

So, when you later do var legend = svg.append("g") , you are actually adding the legend as a child of the above <g> . And this is what you described seeing in dev tools.

One of the consequences is that the translate() transformation that you applied to the external <g> affects the internal <g> (i.e. the translation of the main <g> of legend added to the translation of the external <g> ).

You probably want to separate things like this:

 var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom); var inner = svg.append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 

Then modify your code to draw an existing diagram in inner , not svg .

As a result, var legend = svg.append("g") will add the legend as the sibling of inner , and any translation you apply to legend will refer to the upper left upper corner of svg (unlike inner at the top left, which is translated by margin )

And probably you want to translate legend like this:

 var legend = svg.append("g") .attr("transform", "translate(" + width - margin.right + "," + margin.top + ")"); 

This moves the legend to the right end of the chart, MINUS margin.right . That way you can customize margin.right to create enough space for legend .

Finally, note that the call

 legend .attr("height", 300) .attr("width", 200) 

does nothing, because for svg <g> there is no way to explicitly set the width and height. In any case, this does not mean much, because svg does not have the "streaming" behavior of html layouts. The width and height shown in dev tools are implicit bounds associated with the bounds of <g> child elements. (If necessary, there is a way to get these calculated grades in javascript using the getBBox() function).

+2
source

by looking at the code you specified, you are actually attaching the var legend to your group "g", not "svg",
var legend = svg.append("g")

on this line, you are telling d3 so that your legend variable is added to g, which is added to svg, if I understand correctly, you should try something like this:

 var legend = svg.selectAll(".legend") .enter().append("g") 

create another "g" group for your legends. I apologize for my poor English.

-1
source

Source: https://habr.com/ru/post/1243976/


All Articles