I have a D3 script that creates a circle for every point in my data and then places the circles next to each other. How can I place the circles into a new row after, say, five circles (or after the graphic hits a specific width)? The image below shows what I am trying to accomplish. Code and JS fiddle also included.
Thanks so much for any help that you can provide.
[![enter image description here][1]][1]
Js Fiddle: https://jsfiddle.net/amarton/Ly1go0sp/1/
{
"category": "apple",
"step": 1,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "banana",
"step": 2,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "grape",
"step": 3,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "apple",
"step": 3,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "banana",
"step": 2,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "grape",
"step": 1,
"description": "Lorem ipsum dolor sit amet"
}
]
async function draw(){
//Get data
const data = jsonData;
//apend the svg container to the div
var svgContainer = d3.select("#myChart").append("svg")
.attr("viewBox", `0 0 500 500`)
//append the circles to the svg
var circles = svgContainer.selectAll("circle")
//this calls the data
.data(data)
.enter()
.append("circle")
//draw the circles
var circleAttributes = circles
//increment the x posiiton
.attr("cx", function(e, counter){return 50 * (counter) + 50})
.attr("cy", 30)
.attr("r", 20 )
}
draw()```
[1]: https://i.sstatic.net/nouHP.png
Check if cx
exceeds the viewBox
edge. You can then increase cy
to place the circle in different rows:
// the width and height of the view box
var viewBoxEdge = 500;
// the x/y offset between the center of two circles
var circleOffset = 50;
//apend the svg container to the div
var svgContainer = d3.select("#myChart").append("svg")
.attr("viewBox", `0 0 ${viewBoxEdge} 500`)
// ...
//draw the circles
var circleAttributes = circles
//increment the x posiiton
.attr("cx", function(e, counter){
// this will 'return' cx to the horizontal start of the row when exceeding width - think of this as a 'column index'
var cx = (50 * counter) % (viewBoxEdge - circleOffset) + circleOffset;
return cx;
})
.attr("cy", function(e, counter) {
// this will move the circle to the next row when exceeding the width - think of this as a 'row index'
var cy = Math.floor(50 * counter / (viewBoxEdge - circleOffset)) * circleOffset + circleOffset;
return cy;
})
.attr("r", 20 )
var jsonData = [
{
"category": "apple",
"step": 1,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "banana",
"step": 2,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "grape",
"step": 3,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "apple",
"step": 3,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "banana",
"step": 2,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "grape",
"step": 1,
"description": "Lorem ipsum dolor sit amet"
},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}
]
async function draw(){
//Get data
const data = jsonData;
// the width and height of the view box
var viewBoxEdge = 500;
// the x/y offset between the center of two circles
var circleOffset = 50;
//apend the svg container to the div
var svgContainer = d3.select("#myChart").append("svg")
.attr("viewBox", `0 0 ${viewBoxEdge} 500`)
//append the circles to the svg
var circles = svgContainer.selectAll("circle")
//this calls the data
.data(data)
.enter()
.append("circle")
//draw the circles
var circleAttributes = circles
// increment the x posiiton
.attr("cx", function(e, counter){
// this will 'return' cx to the horizontal start of the row when exceeding width
var cx = (50 * counter) % (viewBoxEdge - circleOffset) + circleOffset;
return cx;
})
.attr("cy", function(e, counter) {
// this will move the circle to the next row when exceeding the width
var cy = Math.floor(50 * counter / (viewBoxEdge - circleOffset)) * circleOffset + circleOffset;
return cy;
})
.attr("r", 20 )
}
draw();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="myChart"></div>
Use HTML Elements
d3
is not limited to SVG manipulation. You can manipulate the entire element tree of html
.
If the visualization you are trying to build can be done in html only, do it in html!
You could use display: flex
to layout the circles over multiple rows.
This has two advantages:
d3
'layout engine'.var jsonData = [
{
"category": "apple",
"step": 1,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "banana",
"step": 2,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "grape",
"step": 3,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "apple",
"step": 3,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "banana",
"step": 2,
"description": "Lorem ipsum dolor sit amet"
},
{
"category": "grape",
"step": 1,
"description": "Lorem ipsum dolor sit amet"
},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}
]
var circlesContainer = d3.select('.circles');
circlesContainer = circlesContainer
.selectAll('.circle')
.data(jsonData)
.enter()
.append('div')
.attr('class', 'circle')
.circles {
display: flex;
width: 100%;
flex-direction: row;
flex-wrap: wrap;
}
.circles .circle {
display: block;
height: 40px;
width: 40px;
border-radius: 20px;
background-color: black;
margin: 3px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="circles"></div>