I am trying to create 3, responsive, bar charts on the same page using the same script with different data.
My problem comes when the window is resized. I keep running into elements that are only updated in the last chart to be rendered.
I apologize for not providing a jsfiddle, but I couldn’t get the fiddle to render my code (fiddle – newbee)
The following snippet of code just produces the axis’. First problem I encounter is with d3.axisBottom(scale) – as an example of what I’m encountering.
Page:
<!DOCTYPE html>
<html lang="en">
<head>
<title>AgeReport 1.3</title>
<script src="jquery-1.11.2.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js">
</script>`
<link rel=stylesheet type=text/css
href="https://cdnjs.cloudflare.com/ajax/libs/twitter-
bootstrap/3.3.5/css/bootstrap.min.css">
<style>
body {
background-color: #e6e6e6;
font-family: sans-serif;
font-size: 12px;
}
section {
position: relative;
padding: 24px;
height: 100%;
}
.section-body:first-child {
margin-top: 24px;
}
.col-md-3, .col-lg-3, .col-md-4, .col-lg-4, .col-md-6,
.col-lg-6 {
padding: 0;
}
.card {
position: relative;
background-color: #F5F5F5;
color: #212121;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
border: 1px solid #BDBDBD;
box-shadow: none;
height: 100%;
}
.card-head.card-head-xs {
line-height: 32px;
min-height: 36px;
}
.card-head {
position: relative;
line-height: 40px;
min-height: 44px;
vertical-align: middle;
border-radius: 2px 2px 0 0;
}
section {
background-repeat: no-repeat;
}
.row {
margin-left: 0;
}
.col-md-4 {
padding-left: 5px;
padding-right: 5px;
}
.card-head {
background-color: #bdbdbd;
}
.card-body {
padding: 10px 10px 10px 10px;
}
/*-- Age Chart --*/
#age-chart-row {
position: relative;
height: 345px;
}
#age-chart-row .card {
border-width: 1px 1px 0 1px;
}
.chart-container {
height: 288px;
border: 1px solid #bcbcbc;
background-color: white;
}
.age-chart-canvas {
position: absolute;
width: 100%;
height: 100%;
}
.axis--y .tick line {
stroke: lightgray;
}
</style>
<script type="text/javascript">
var ageDistributionData = {....}'
$(document).ready(function() {
var vData = ageDistributionData;
var dData = ageDistributionData;
var mData = ageDistributionData;
var vChart = new AgeChart("#vChart", vData);
var dChart = new AgeChart("#dChart", dData);
var mChart = new AgeChart("#mChart", mData);
});
</script>
</head>
<body>
<section>
<div class="section-body">
<div id="age-chart-row" class="row">
<div class="col-md-4">
<div class="card">
<div class="card-head card-head-xs"></div>
<div class="card-body">
<div id="vChart" class="chart-container"></div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-head card-head-xs"></div>
<div class="card-body">
<div id="dChart" class="chart-container"></div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-head card-head-xs"></div>
<div class="card-body">
<div id="mChart" class="chart-container"></div>
</div>
</div>
</div>
</div>
</div>
</section>
</body>
</html>
Chart script:
function AgeChart(container, data) {
var containerW, containerH, chartW, chartH, svg, chartG,
xScale, x, y, gAxis, xAxis, yAxis, xBottom, bars;
var margin = {top: 20, right: 10, bottom: 50, left: 60};
//-- axis labels -> will need translating --
var yLabelStr = "Anzahl",
xLabelStr = "Alter";
// parse raw data
var agedData = parseAgedData(data),
sizeMax = data.AgeDistribution.DataOwnerSizeMax,
countMax = 0;
for (var i = 0; i < agedData.length; i++) {
if(agedData[i].doCount > countMax) {
countMax = agedData[i].doCount;
}
};
// init scales
var xScale = d3.scaleBand()
.paddingInner(0.16)
.paddingOuter(0.1);
xScale.domain(agedData.map(function (d) {
return d.label;
}));
x = d3.scaleBand()
.paddingInner(0.16)
.paddingOuter(0.1);
x.domain(agedData.map(function (d, i) {
return i + 1;
}));
y = d3.scaleLinear();
y.domain([0, countMax]);
// init containers
svg = d3.select(container)
.append("svg")
.attr("id", container.substring(1) + "Svg")
.classed("age-chart-canvas", true);
chartG = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// init axis lines
yAxis = chartG.append("g")
.attr("class", "axis axis--y");
gAxis = chartG.append("g")
.attr("class", "axis axis--x");
xAxis = gAxis.append("line")
.style("stroke", "black")
.attr("x1", 0)
.attr("x2", 0)
.classed("x-domain", true);
// render chart
render();
function render() {
updateDimensions();
// set tick lines
yAxis.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text(function(d) {
return d;
});
d3.selectAll("g.axis--y g.tick line")
.attr("x1", function(d) {
return chartW;
});
gAxis.attr("transform", "translate(0," + chartH + ")")
.call(new d3.axisBottom(xScale));
gAxis.selectAll(".domain").remove();
d3.selectAll(".x-domain")
.attr("x2", function(d) {
return chartW;
});
d3.selectAll("g.axis--x g.tick line")
.attr("y2", function(d, i) {
if(i%2 == 0)
return 6;
else
return 16;
})
.style("fill", "#bdbdbd");
d3.selectAll("g.axis--x g.tick text")
.attr("y", function(d, i) {
if(i%2 == 0)
return 9;
else
return 20;
});
};
function updateDimensions() {
containerW = getDimension(container, "width");
containerH = getDimension(container, "height");
chartW = containerW - margin.left - margin.right;
chartH = containerH - margin.top - margin.bottom;
// set axis scales
xScale.rangeRound([0, chartW]);
x.rangeRound([0, chartW]);
y.rangeRound([chartH, 0]);
};
d3.select(window).on("resize", render);
};
Thanks!
2
Answers
I seem to have found a solution. After boning up on Javascript objects, I re-wrote my code to create and return a chart object that encapsulates the chart properties and methods. I can then call the render method in multiple chart objects to update them individually.
the new chart script:
then on my page I call render() when the window is resized and all charts are updated independently...
Any tips or suggestions are most welcome.
I was also stuck on this same issue. I know you found a solution but I thought I’d offer what I found in case it helps someone else who ends up at this page, since this question more accurately described my problem than anything else I’ve found.
My solution finally came from the following Stack Overflow post. The answer there explains that you need to add a namespace to the listener, otherwise the existing listener will be removed when a new one is added.
How to have multiple d3 window resize events
For this example, I had to change this code:
to
where scope.chartId was a unique id for each chart so that the listener was also unique and would not be removed when a new chart was added.