In D3.js how can I create a horizontal legend with 2 rows?
This is current JavaScript code that creates a legend:
var legendGroup = svg.append(\"g\")
The code you posted is a column, as you stated, because it translates each piece of the legend 20 pixels below the last: "translate(0," + 20*i + ")"
. By manipulating the translate statement you can create a 2 row legend if you know how many elements are in your array, though even if you don't, you can specify how many elements can be in each row.
In the following code block, n
refers to how many items should be in a row, itemWidth
a pixel value for the width of each legend entry, and itemHeight
the height.
I don't know how many legend items you have, but you could set it up as such:
.attr("transform", function(d,i) { return
"translate(" + i%n * itemWidth +
"," +
Math.floor(i/n) * itemHeight + ")"; })
This should work like so:
var data = ["Cat A","Cat B","Cat C", "Cat D", "Dog A", "Dog B", "Dog C", "Dog D"];
var n = data.length/2;
var itemWidth =80;
var itemHeight = 18;
var svg = d3.select("svg");
var color = d3.scale.category10();
var legend = svg.selectAll(".legend")
.data(data)
.enter()
.append("g")
.attr("transform", function(d,i) { return "translate(" + i%n * itemWidth + "," + Math.floor(i/n) * itemHeight + ")"; })
.attr("class","legend");
var rects = legend.append('rect')
.attr("width",15)
.attr("height",15)
.attr("fill", function(d,i) { return color(i); });
var text = legend.append('text')
.attr("x", 15)
.attr("y",12)
.text(function(d) { return d; });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="500" height="100"></svg>
Edit: Here's a snippet moving the entire horizontal legend (using four rows to show the movement better to the right side better in this narrow view) based on the comment below:
var data = ["Cat A","Cat B","Cat C", "Cat D", "Dog A", "Dog B", "Dog C", "Dog D"];
var n = data.length/4;
var itemWidth =80;
var itemHeight = 18;
var width = 500;
var svg = d3.select("svg");
var color = d3.scale.category10();
var legendGroup = svg.append("g")
.attr("transform", "translate("+(width-150)+",10)");
var legend = legendGroup.selectAll(".legend")
.data(data)
.enter()
.append("g")
.attr("transform", function(d,i) { return "translate(" + i%n * itemWidth + "," + Math.floor(i/n) * itemHeight + ")"; })
.attr("class","legend");
var rects = legend.append('rect')
.attr("width",15)
.attr("height",15)
.attr("fill", function(d,i) { return color(i); });
var text = legend.append('text')
.attr("x", 15)
.attr("y",12)
.text(function(d) { return d; });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="500" height="200"> </svg>
const legendData = ['The United States', 'Brazil', 'India', 'China', 'United Arab Emirates'] // The names which you want to appear as legends should be inside this array.
const legend = d3.select('svg')
.append('g')
.attr('transform', 'translate(' + (margin.left + margin.right + 60) + ',' + (margin.top - 20) + ')')
.selectAll('g')
.data(legendData)
.enter()
.append('g');
legend.append('rect')
.attr('fill', (d, i) => color(d)) // const color = d3.scaleOrdinal(d3.schemeCategory10);
.attr('height', 15)
.attr('width', 15);
legend.append('text')
.attr('x', 18)
.attr('y', 10)
.attr('dy', '.15em')
.text((d, i) => d)
.style('text-anchor', 'start')
.style('font-size', 12);
// Now space the groups out after they have been appended:
const padding = 10;
legend.attr('transform', function (d, i) {
return 'translate(' + (d3.sum(legendData, function (e, j) {
if (j < i) { return legend.nodes()[j].getBBox().width; } else { return 0; }
}) + padding * i) + ',0)';
});