const canvas = d3.select('#stacked');
const stackedSvg = canvas.append('svg')
.attr('preserveAspectRatio', 'xMinYMin meet')
.attr('viewBox', '0 0 900 500')
.classed('svg-content', true);
let margin = {top: 40, right: 30, bottom: 20, left: 70},
width = 260 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
// append the svg object to the body of the page
let svg = stackedSvg.append('g')
.attr('width', width)
.attr('height', height)
.attr('transform', `translate(${margin.left}, ${margin.top})`);
//number formater
const formater = d3.format(',d');
let myData = [
{
"group": "Field 1",
"0-45": 12345,
"46-65": 91568,
"66-85": 13087,
"85+": 7045
},
{
"group": "Field 2",
"0-45": 62345,
"46-65": 15347,
"66-85": 37688,
"85+": 7007
},
{
"group": "Field 3",
"0-45": 11457,
"46-65": 28456,
"66-85": 124564,
"85+": 7564
},
{
"group": "Field 4",
"0-45": 19234,
"46-65": 26754,
"66-85": 14153,
"85+": 7453
}
]
//tooltip
let tooltip = d3.select('body').append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
// Parse the Data
Promise.resolve(myData)
.then(data => {
// console.log(data);
//select the size bands
let keys = d3.keys(data[0]).slice(1);
// console.log(keys);
// List of groups in JSON. value of the first column called group
let groups = d3.map(data, function(d){return(d.group)}).keys();
// console.log(groups);
// X axis
let x = d3.scaleBand()
.domain(groups)
.range([0, width])
.padding([0.2])
svg.append('g')
.attr('transform', 'translate(0,' + height + ')')
.call(d3.axisBottom(x)
.tickSizeOuter(0));
svg.append('text')
.attr('class', 'xLabel')
.attr('text-anchor', 'end')
.attr('x', width / 2 + 20)
.attr('y', height + 30)
.text('Size Band')
.style('font-size', 10);
// Y axis
let y = d3.scaleLinear()
.domain([0, d3.max(data, d => d3.sum(keys, k => +d[k]))])
.range([ height, 0 ]);
svg.append('g')
.call(d3.axisLeft(y)
.tickSizeOuter(0)
.tickSizeInner(- width)
.tickPadding(5))
.selectAll(".tick")
.style("stroke-dasharray", ("1, 1")) //dash line across graph.
.each(function (d, i) {
if ( d == 0 ) {
this.remove();
}
});
svg.append('text')
.attr('class', 'yLabel')
.attr('transform', 'rotate(-90)')
.attr('y', - 40)
.attr('x', - height / 2 + 20)
.attr('text-anchor', 'end')
.text('Units')
.style('font-size', 10);
// color
let color = d3.scaleOrdinal()
.domain(keys)
.range(['brown', 'steelblue', 'olivedrab', 'darkorange']);
//stack the data --> stack per subgroup
let stackedData = d3.stack()
.keys(keys)
(data);
console.log(stackedData)
let totalColVal = d3.nest()
.key(function(d){return (d.group)})
.rollup(function(totals){
return d3.sum(totals, function(d) {return d3.sum(d3.values(d))})
}).entries(data)
console.log(totalColVal)
//tooltip
let mouseover = function(d, i) {
let subgroupName = d3.select(this.parentNode).datum().key;
// console.log(subgroupName)
let subgroupValue = d.data[subgroupName];
// console.log(subgroupValue)
tooltip.html('<b>Field:</b> <span style="color:yellow">' + d.data.group + '</span>' + '<br>\
' + '<b>Count:</b> <span style="color:yellow">' + formater(subgroupValue) + '</span>' + '<br>\
' + ' <b>Size Band:</b> <span style="color:yellow">' + subgroupName + '</span>' + '<br>\
' + '<b>Field Total:</b> <span style="color:yellow">' + totalColVal[i].value + '</span>')
.style("opacity", 1)
};
let mousemove = function(d) {
tooltip.style('left', (d3.event.pageX - 70) + 'px')
.style('top', (d3.event.pageY - 85) + 'px')
};
let mouseleave = function(d) {
tooltip.style("opacity", 0)
};
// Show the bars
svg.append('g')
.selectAll('g')
// Enter in the stack data = loop key per key = group per group
.data(stackedData)
.enter().append('g')
.attr('fill', function(d) { return color(d.key) })
.selectAll('rect')
// enter a second time = loop subgroup per subgroup to add all rectangles
.data(function(d) { return d; })
.enter().append('rect')
.on('mouseover', mouseover)
.on('mousemove', mousemove)
.on('mouseleave', mouseleave)
.transition()
.attr('y', d => y(d.height))
.delay(function(d, i) {
return i * 100
})
.ease(d3.easeLinear)
.attr('x', function(d) { return x(d.data.group); })
.attr('y', function(d) { return y(d[1]); })
.attr('height', function(d) { return y(d[0]) - y(d[1]); })
.attr('width',x.bandwidth())
.style('stroke', 'black')
.style('opacity', 0.9);
});
body {
font-family: halyard-display, sans-serif;
/* background-color: black; */
}
div.tooltip {
position: absolute;
text-align: left;
width: fit-content;
height: fit-content;
padding: 5px 5px;
font-size: 16px;
background:rgb(24, 23, 23);
color: white;
border-radius: 5px;
pointer-events: none;
}
.yLabel {
fill: #DEDC00;
font-style: italic;
font-weight: 600;
}
.xLabel {
fill: #DEDC00;
font-style: italic;
font-weight: 600;
}
g .tick text {
font-size: 8px;
color: grey;
}
g .tick text:hover {
font-size: 12px;
}
g .tick {
color: #DEDC00;
}
<script src="https://d3js.org/d3.v5.js"></script>
<div id="stacked"></div>