Trying to implement border for selected bar in d3 stack bar chart. Here the first bar's top border goes behind second bar a little bit. How to avoid this?
var svg, height, width, margin, parentWidth, parentHeight;
// container size
parentWidth = 700;
parentHeight = 500;
margin = {top: 50, right: 20, bottom: 35, left: 30};
width = parentWidth - margin.left - margin.right;
height = parentHeight - margin.top - margin.bottom;
var selectedSection = window.sessionStorage.getItem('selectedSection');
// data
var dataset = [{"label":"DEC","Set Up":{"count":12,"id":1,"label":"Set Up","year":"2016","graphType":"setup"},"Not Set Up":{"count":12,"id":0,"label":"Not Set Up","year":"2016","graphType":"setup"}},{"label":"JAN","Set Up":{"count":6,"id":1,"label":"Set Up","year":"2017","graphType":"setup"},"Not Set Up":{"count":21,"id":0,"label":"Not Set Up","year":"2017","graphType":"setup"}},{"label":"FEB","Set Up":{"count":1,"id":1,"label":"Set Up","year":"2017","graphType":"setup"},"Not Set Up":{"count":2,"id":0,"label":"Not Set Up","year":"2017","graphType":"setup"}},{"label":"MAR","Set Up":{"count":0,"id":1,"label":"Set Up","year":"2017","graphType":"setup"},"Not Set Up":{"count":0,"id":0,"label":"Not Set Up","year":"2017","graphType":"setup"}},{"label":"APR","Set Up":{"count":0,"id":1,"label":"Set Up","year":"2017","graphType":"setup"},"Not Set Up":{"count":0,"id":0,"label":"Not Set Up","year":"2017","graphType":"setup"}}];
// x cord
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], 0.2);
// color helper
var colorRange = d3.scale.category20();
var color = d3.scale.ordinal()
.range(colorRange.range());
// x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
var colors = ['#50BEE9', '#30738C'];
// Set SVG
svg = d3.select('#chart')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom )
.attr('class', 'setup')
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
color.domain(d3.keys(dataset[0]).filter(function(key) { return key !== 'label'; }));
dataset.forEach(function(d) {
var y0 = 0;
d.values = color.domain().map(function(name) {
return {
name: name,
y0: y0,
y1: y0 += +d[name].count,
patientStatus:d[name].id,
graphType:d[name].graphType,
fromDate:{
month:d.label,
year:d[name].year
},
toDate:{
month:d.label,
year:d[name].year
}
};
});
d.total = d.values[d.values.length - 1].y1;
});
var y = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d.total;
})])
.range([height, 0]);
var ticks = y.ticks(),
lastTick = ticks[ticks.length-1];
var newLastTick = lastTick + (ticks[1] - ticks[0]);
if (lastTick<y.domain()[1]){
ticks.push(lastTick + (ticks[1] - ticks[0]));
}
// adjust domain for further value
y.domain([y.domain()[0], newLastTick]);
// y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
.tickSize(-width, 0, 0)
.tickFormat(d3.format('d'))
.tickValues(ticks);
x.domain(dataset.map(function(d) { return d.label; }));
y.domain([0, d3.max(dataset, function(d) { return d.total; })]);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'y axis')
.call(yAxis);
var bar = svg.selectAll('.label')
.data(dataset)
.enter().append('g')
.attr('class', 'g')
.attr('id', function(d, i) {
return i;
})
.attr('transform', function(d) { return 'translate(' + x(d.label) + ',0)'; });
var barEnter = bar.selectAll('rect')
.data(function(d) { return d.values; })
.enter();
barEnter.append('rect')
.attr('width', x.rangeBand())
.attr('y', function(d) {
return y(d.y1);
})
.attr('class', function(d, i){
return 'bar';
})
.attr('height', function(d) { return y(d.y0) - y(d.y1); })
.style('fill', function(d,i) { return colors[i]; })
.on('click', function(d, i) {
d3.selectAll('.bar').classed('selected', false);
d3.select(this)
.classed('bar selected', true);
});
barEnter.append('text')
.text(function(d) {
var calcH = y(d.y0) - y(d.y1);
var inText = (d.y1-d.y0);
if(calcH >= 20) {
return inText;
} else {
return '';
}
})
.attr('class','inner-text')
.attr('y', function(d) { return y(d.y1)+(y(d.y0) - y(d.y1))/2 + 5; })
.attr('x', function(){
return (x.rangeBand()/2) - 10;
});
svg
.select('.y')
.selectAll('.tick')
.filter(function (d) {
return d % 1 !== 0;
})
.style('display','none');
svg
.select('.y')
.selectAll('.tick')
.filter(function (d) {
return d === 0;
})
.select('text')
.style('display','none');
Tag:
Script:
Css:
In a SVG, just like a real painter putting ink to a white canvas, the element that is painted last stays on top.
Right now, the behaviour you're seeing is the expected one, because each stacked bar (rectangle) is in a different
<g>
element, and the groups, of course, have a given order in the SVG structure.The solution involves just one line:
What this line does is selecting the group of the clicked rectangle and raising it (that is, moving it down in the DOM tree), so that group will be on top of all others. According to the API, raise():
"Moving down", "be on top" and "be the last child" may be a bit confusing and seem contradictory, but here is the explanation. Given this SVG structure:
<baz>
, being the last element, is the one painted last, and it is the element visually on the top in the SVG. So, raising an element means moving it down in the SVG tree structure, but moving it up visually speaking.Here is your updated fiddle: https://jsfiddle.net/86Lgaupt/
PS: I increased the stroke-width just to make visibly clear that the clicked rectangle is now on top.