Example tweaked from original example by Scott Murray.
use div, class & id to layout page with HTML
use d3.select, .node().clientWidth to ensure svg width is consistent with css
use d3.select to append svg to correct chartContainer (id), ids have to be unique
any d3 javascript will add its output underneath whatever is already inside that div i.e. after
dynamically change sort button input
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<title>D3: Dynamically sized charts happily coexist side-by-side</title>
<script type="text/javascript" src="d3.js"></script>
<style type="text/css">
/* Lots of new CSS rules! */
/* HTML page styles */
* {
margin: 0;
padding: 0;
}
body {
font-family: Helvetica, Arial, sans-serif;
background-color: #eee;
}
#container {
width: 800px;
margin: 25px auto 25px auto;
padding: 50px 50px 50px 50px;
background-color: white;
box-shadow: 0 0 20px #ccc;
}
h1 {
margin-bottom: 25px;
font-size: 24px;
}
h2 {
margin-top: 30px;
font-size: 14px;
}
p {
margin-bottom: 25px;
font-size: 14px;
line-height: 18px;
}
.chartContainer {
/* Place the chart containers side-by-side! */
display: inline-block;
width: 49%;
}
#buttonContainer {
margin-bottom: 10px;
}
#footer p {
margin-top: 50px;
margin-bottom: 0;
text-align: right;
font-size: 10px;
color: gray;
}
/* Chart styles */
svg {
display: block;
margin-bottom: 10px;
background-color: white;
}
g.bar {
cursor: pointer;
}
g.bar text {
font-family: sans-serif;
font-size: 10px;
fill: black;
font-style: bold;
text-anchor: middle;
opacity: 0;
}
g.bar.highlight text {
opacity: 1;
}
g.bar.highlight rect {
fill: #ff0;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
/* Button styles */
button {
padding: 15px;
display: inline-block;
white-space: nowrap;
background-color: #ccc;
background-image: linear-gradient(top, #eee, #ccc);
filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#eeeeee', EndColorStr='#cccccc');
border: 1px solid #777;
padding: 0 1.5em;
margin: 0.5em;
font: bold 1em/2em Arial, Helvetica;
text-decoration: none;
color: #333;
text-shadow: 0 1px 0 rgba(255,255,255,.8);
border-radius: .2em;
box-shadow: 0 0 1px 1px rgba(255,255,255,.8) inset, 0 1px 0 rgba(0,0,0,.3);
}
button:hover {
background-color: #ddd;
background-image: linear-gradient(top, #fafafa, #ddd);
filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa', EndColorStr='#dddddd');
}
</style>
</head>
<body>
<!-- New HTML structure and content! -->
<div id="container">
<h1>A generic but punchy title goes here</h1>
<p>Laying out charts side by side isn’t hard as long as you pay extra attention to your CSS and you carefully consider how to best use divs, classes and ids. </p>
<div class="chartContainer" id="salesChartContainer">
<h2>Giving</h2>
</div>
<div class="chartContainer" id="bonusChartContainer">
<h2>Receiving</h2>
</div>
<div id="buttonContainer">
<button id="sort">Sort by Total</button>
</div>
<div id="footer">
<p><strong>Note:</strong> This is where the footer goes with some extra all-important information.</p>
</div>
</div>
<script type="text/javascript">
//Sort button state
//Default action for button will be to sort by *value*
var sortByNameOrValue = false;
//New, dynamic width value pulled from .chartContainer
var w = d3.select(".chartContainer").node().clientWidth;
//Height, padding
var h = 250;
var padding = 35;
//Sample data
var dataset = [
{"name":"A","sales":1520,"bonus":20},
{"name":"B","sales":656,"bonus":340},
{"name":"C","sales":182,"bonus":87},
{"name":"D","sales":187,"bonus":242},
{"name":"E","sales":35,"bonus":47},
{"name":"F","sales":193,"bonus":321},
{"name":"G","sales":56,"bonus":320},
{"name":"H","sales":61,"bonus":447},
{"name":"I","sales":21,"bonus":210},
{"name":"J","sales":26,"bonus":420},
{"name":"K","sales":28,"bonus":511}];
//Configure x and y scale functions
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([ padding, w - padding ], 0.05);
//Now using two different y scales for two different charts
var salesScale = d3.scale.linear()
.domain([ 0, d3.max(dataset, function(d) {
return d.sales;
}) ])
.rangeRound([ h - padding, padding ]);
var bonusScale = d3.scale.linear()
.domain([ 0, d3.max(dataset, function(d) {
return d.sales; // made this equal to max in sales for comparison
}) ])
.rangeRound([ h - padding, padding ]);
//Now using two different y axes
var salesAxis = d3.svg.axis()
.scale(salesScale)
.orient("left")
.ticks(5)
.outerTickSize(0);
var bonusAxis = d3.svg.axis()
.scale(bonusScale)
.orient("left")
.ticks(5)
.outerTickSize(0);
//
// Make the first chart (sales data)
//
//Create SVG element
var svg = d3.select("#salesChartContainer") //New target location!
.append("svg")
.attr("id", "salesChart")
.attr("width", w)
.attr("height", h);
//Create groups
var groups = svg.selectAll("g")
.data(dataset)
.enter()
.append("g")
.attr("class", "bar")
.attr("transform", function(d, i) {
return "translate(" + xScale(i) + ",0)";
});
//Add bar to each group
var rects = groups.append("rect")
.attr("x", 0)
.attr("y", function(d) {
return h - padding;
})
.attr("width", xScale.rangeBand())
.attr("height", 0)
.attr("fill", "#8e0002");
//Add label to each group
groups.append("text")
.attr("x", xScale.rangeBand() / 2)
.attr("y", function(d) {
return salesScale(d.sales) - 3;
})
.text(function(d) {
return d.sales;
})
//Add second piece of text - remove css opacity directly
// height is related to height-padding of svg
groups.append("text")
.attr("x", xScale.rangeBand() / 2)
.attr("y", [h-(padding/1.5)])
.text(function(d) {
return d.name;
})
.style("opacity", 1)
//Transition rects into place
rects.transition()
.delay(function(d, i) {
return i * 100;
})
.duration(1500)
.attr("y", function(d) {
return salesScale(d.sales);
})
.attr("height", function(d) {
return h - padding - salesScale(d.sales);
});
//Create y axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding + ",0)")
.attr("opacity", 0)
.call(salesAxis)
.transition()
.delay(2000)
.duration(1500)
.attr("opacity", 1.0);
//
// Make the second chart (bonus data)
//
//Create SVG element
svg = d3.select("#bonusChartContainer") //New target location!
.append("svg")
.attr("id", "bonusChart")
.attr("width", w)
.attr("height", h);
//Create groups
groups = svg.selectAll("g")
.data(dataset)
.enter()
.append("g")
.attr("class", "bar")
.attr("transform", function(d, i) {
return "translate(" + xScale(i) + ",0)";
});
//Add bar to each group
rects = groups.append("rect")
.attr("x", 0)
.attr("y", function(d) {
return h - padding;
})
.attr("width", xScale.rangeBand())
.attr("height", 0)
.attr("fill", "#ff5000");
//Add label to each group
groups.append("text")
.attr("x", xScale.rangeBand() / 2)
.attr("y", function(d) {
return bonusScale(d.bonus) - 3;
})
.text(function(d) {
return d.bonus;
})
//Add second piece of text - remove css opacity directly
// height is related to height-padding of svg
groups.append("text")
.attr("x", xScale.rangeBand() / 2)
.attr("y", [h-(padding/1.5)])
.text(function(d) {
return d.name;
})
.style("opacity", 1)
//Transition rects into place
rects.transition()
.delay(function(d, i) {
return i * 100;
})
.duration(1500)
.attr("y", function(d) {
return bonusScale(d.bonus);
})
.attr("height", function(d) {
return h - padding - bonusScale(d.bonus);
});
//Create y axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding + ",0)")
.attr("opacity", 0)
.call(bonusAxis)
.transition()
.delay(2000)
.duration(1500)
.attr("opacity", 1.0);
//New functionality for interaction for ALL groups
//in BOTH charts
d3.selectAll("g.bar")
.on("mouseover", function(d) {
var thisName = d.name;
d3.selectAll("g.bar")
.filter(function(d) {
if (thisName == d.name) {
return true; //…then it's a match
}
})
.classed("highlight", true);
})
.on("mouseout", function() {
d3.selectAll("g.bar")
.classed("highlight", false);
})
//Sorting logic
d3.select("#sort")
.on("click", function() {
//Need to reselect all groups in each chart
d3.selectAll("#salesChart g.bar").sort(function(a, b) {
if (sortByNameOrValue) {
return d3.ascending(a.name, b.name);
} else {
return d3.descending(a.sales, b.sales);
}
})
.transition()
.delay(function(d, i) {
return i * 50;
})
.duration(1000)
.attr("transform", function(d, i) {
return "translate(" + xScale(i) + ",0)";
});
//Need to reselect all groups in each chart
d3.selectAll("#bonusChart g.bar").sort(function(a, b) {
if (sortByNameOrValue) {
return d3.ascending(a.name, b.name);
} else {
return d3.ascending(a.bonus, b.bonus);
}
})
.transition()
.delay(function(d, i) {
return i * 50;
})
.duration(1000)
.attr("transform", function(d, i) {
return "translate(" + xScale(i) + ",0)";
});
//Update text in button
d3.select(this)
.text(function() {
if (sortByNameOrValue) {
return "Sort by Total";
} else {
return "Sort by Rank";
}
})
//Flip value of boolean
sortByNameOrValue = !sortByNameOrValue;
});
</script>
</body>
</html>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js