This example I made for Advance Local, a media group that has collects data on users of many different media outlets. Color scales are used to differentiate between different categories, and custom transitions create a nice smooth for comparing different media outlets.
<!DOCTYPE html>
<!-- Paul Buffa 2015
-->
<meta charset="utf-8">
<link href='//fonts.googleapis.com/css?family=Lato&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<style>
body {
font: 10px Lato;
}
select {
color: #000;
font-size: 14px;
font-weight: bold;
padding: 2px 10px;
width: 378px;
}
p {
font: 14px Lato;
margin-left: 100px;
max-width: 800px;
}
#disclaimer {
font: 10px Lato;
max-width: 300px;
}
h1 {
font: 26px Lato ;
margin-left: 100px;
font-weight: bold;
}
#selectSpot {
margin-left: 100px;
}
#areaList {
margin-left: 95px;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x {
font-size: 0;
}
rect {
stroke: #000;
stroke-opacity: .5;
stroke-width: .1;
fill-opacity: .8;
}
</style>
<body>
<h1> We Have Engaged Audiences</h1>
<p>
Our data-driven approach to Audience Targeting enables us to reach everyone from hyper-local auto intenders to huge national audiences with interest and purchase intent in products and services just like yours. We have thousands of pre-built audiences, but if you require a custom, highly targeted audience to grow your business just ask: we can likely find a way to reach your best customers and prospects, wherever they are.
</p>
<div id='selectSpot'>
</div>
<div id='areaList'>
</div>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/numeral.js/1.4.5/numeral.min.js"></script>
<script>
var areas = ['MLive.com','NJ.com','AL.com & gulflive.com','OREGONLIVE','NOLA.com','cleveland.com','syracuse','PennLive','MassLive','SILive.com' ,'Advance Local'],
// CHANGE VERSION HERE
version = 'Advance Local';
var margin = {top: 20, right: 150, bottom: 100, left: 100},
width = 1050 - margin.left - margin.right,
height = 350 - margin.top - margin.bottom,
recWidth = 15;
var x = d3.scale.ordinal()
.rangeBands([0,width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var colorScale = d3.scale.category10();
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Background Rect
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill-opacity", .05);
// Load in Market Data
d3.tsv("data.tsv", function(error, data) {
data.forEach(function(d) {
d['Total'] = +d['Total']
d['Aud'] = d['Audience'].replace(/\s+/g, '').replace(/\W/g, '');
d['Cat'] = d['Category'].replace(/\s+/g, '').replace(/\W/g, '');
});
var nestedData = d3.nest()
.key(function(d) {return d.Type;})
.sortKeys(d3.descending)
.sortValues(function(a,b) {return b['Total']-a['Total'];})
.entries(data);
var selectedData = nestedData.filter(function(d) { return d.key === version; })
console.log(nestedData)
// Setting X & Y Domains based on the data
y.domain(d3.extent(selectedData[0].values.map(function(d, i) { return d.Total; }))).nice();
x.domain(selectedData[0].values.map(function(d) { return d.Audience; }));
svg.selectAll(".bar")
.data(selectedData[0].values)
.enter().append("rect")
.attr("class", function(d) { return "bar " + d.Aud + " " + d.Cat})
.attr("rx", 2)
.attr("ry", 2)
.attr("x", function(d) { return x(d.Audience); })
.attr("y", function(d) { return y(d.Total); })
.attr("height", function(d) { return height - y(d.Total); })
.attr("width", recWidth)
.style("fill", function(d) { return colorScale(d.Category); });
// Axis Labels
svg.selectAll("text")
.data(selectedData[0].values)
.enter().append("text")
.attr("class", function(d) { return "axisText " + d['Aud']})
.attr("y", height + 14)
.attr("text-anchor", "top")
.attr("transform",function(d) { return "rotate(45 " + x(d['Audience']) + "," + (height + 14) + ")";})
.attr("x", function(d, i) { return x(d['Audience']); })
.text(function(d) { return d['Audience']});
// appending the axes
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// Need to make these mobile compatitble
svg.append("text")
.attr("class","label")
.attr("x", -40)
.attr("y", 30)
.attr("text-anchor", "top")
.attr("transform",function(d) { return "rotate(270 " + 10 + "," + (height / 2) + ")";})
.style("font-size","12px")
.text("Unique Monthly Visitors");
svg.append("text")
.attr("class","label")
.attr("x", width - 10)
.attr("y", - 10)
.attr("text-anchor", "end")
.style("font-size","12px")
.text("Click to filter by category");
// Dropdown Filter
d3.select("#selectSpot")
.append("text")
.attr("class","label")
.attr("text-anchor", "top")
.style("font-size","12px")
.text("Click to see how our audiences shift across the country.");
var select = d3.select("#areaList")
.append("select")
.on("change", change),
options = select.selectAll('option').data(areas); // Data join
// Enter selection
options.enter().append("option").text(function(d) { return d; });
// Change the dataset
function change() {
// turn off pointer events
d3.selectAll(".bar")
.style("pointer-events","None");
// Retrieve dropdown selection
var newValue = this.value;
newData = nestedData.filter(function(d) { return d.key === newValue; })
// Reset domains
y.domain(d3.extent(newData[0].values.map(function(d, i) { return d.Total; }))).nice();
x.domain(newData[0].values.map(function(d) { return d.Audience; }));
// Replot bars using update method
svg.selectAll(".bar")
.data(newData[0].values)
.transition()
.delay(function(d,i) {return i * 20;})
.duration(1000)
.attr("class", function(d) { return "bar " + d['Aud'] + " " + d['Cat']})
.attr("width",recWidth)
.attr("height", function(d,i) { return height - y(d['Total']); })
.style("fill", function(d,i) { return colorScale(d['Category']); })
.attr("rx", 2)
.attr("ry", 2)
.attr("y", function(d,i) { return y(d['Total']); })
.attr("x", function(d,i) { return x(d['Audience']); })
.each("end", function() { d3.selectAll(".bar").style("pointer-events","all"); })
// Axis Labels
svg.selectAll("text")
.data(newData[0].values)
.transition()
.duration(1000)
.attr("class", function(d) { return "axisText " + d['Aud']})
.attr("y", height + 14)
.attr("text-anchor", "top")
.attr("transform",function(d) { return "rotate(45 " + x(d['Audience']) + "," + (height + 14) + ")";})
.attr("x", function(d, i) { return x(d['Audience']); })
.text(function(d) { return d['Audience']});
// Reset axes
svg.select(".y")
.attr("class", "y axis")
.transition()
.duration(1000)
.call(yAxis);
svg.select(".x")
.attr("class", "x axis")
.transition()
.delay(function(d,i) {return i * 20;})
.duration(1000)
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
}
// Hover Events
function hover(d,i) {
svg.selectAll(".bar")
.transition()
.delay(100)
.duration(2000)
.ease("elastic")
.attr("height", 10)
.attr("y",height - 10)
.style("fill-opacity",.3);
var textFill = d['Audience'];
textFillTwo = d['Category'],
textFillThree = "Audience of " + numeral(d['Total']).format('0,0'),
xLoc = d['Audience'],
yLoc = d['Total'];
d3.selectAll(".bar" + d['Aud'])
.transition()
.duration(2000)
.attr("height", function(d,i) { return height - y(yLoc); })
.attr("y", function(d,i) { return y(yLoc); })
.style("fill-opacity",1);
d3.selectAll("." + d['Aud'])
.transition()
.duration(500)
.style("font-size","12px");
svg.append("text")
.attr("class","box")
.attr("x", function(d,i) { if(y(yLoc) < (height/3)) { return x(xLoc) + 25; } else { return x(xLoc); }})
.attr("y", function(d,i) { if(y(yLoc) < (height/3)) { return y(yLoc) + 15; } else { return y(yLoc) - 45; }})
.attr("text-anchor", "top")
.style("font-size","18px")
.text(textFill);
svg.append("text")
.attr("class","box")
.attr("x", function(d,i) { if(y(yLoc) < (height/3)) { return x(xLoc) + 25; } else { return x(xLoc); }})
.attr("y", function(d,i) { if(y(yLoc) < (height/3)) { return y(yLoc) + 35; } else { return y(yLoc) - 25; }})
.attr("text-anchor", "top")
.style("font-size","18px")
.text(textFillTwo);
svg.append("text")
.attr("class","box")
.attr("x", function(d,i) { if(y(yLoc) < (height/3)) { return x(xLoc) + 25; } else { return x(xLoc); }})
.attr("y", function(d,i) { if(y(yLoc) < (height/3)) { return y(yLoc) + 55; } else { return y(yLoc) - 5; }})
.attr("text-anchor", function(d,i) { if(y(yLoc) < (height/3)) { return "end"; } else { "top"; }})
.attr("text-anchor", "top")
.style("font-size","18px")
.text(textFillThree);
}
function hoverOut() {
d3.selectAll(".box").remove();
d3.selectAll(".bar").transition().delay(100).style("pointer-events","none");
svg.selectAll(".bar")
.transition()
.duration(2000)
.ease("elastic")
.attr("height", function(d,i) { return height - y(d['Total']); })
.attr("y", function(d,i) { return y(d['Total']); })
.style("fill-opacity",1)
.each("end", function() { d3.selectAll(".bar").style("pointer-events","all"); });
svg.selectAll(".axisText")
.transition()
.duration(500)
.ease("linear")
.style("font-size","10px");
}
svg.selectAll(".bar")
.on("mouseover", hover)
.on("mouseout", hoverOut);
// Click on Legend to "Filter" Dataset
function legendClick() {
// Removing non-alphabetic characters for selection
var filterAud = this.__data__.replace(/\s+/g, '').replace(/\W/g, '');
svg.selectAll(".bar")
.transition()
.duration(2000)
.ease("elastic")
.attr("height", 10)
.attr("y",height - 10)
.style("fill-opacity",.3);
svg.selectAll("." + filterAud)
.transition()
.duration(2000)
.ease("elastic")
.attr("height", function(d,i) { return height - y(d['Total']); })
.attr("y", function(d,i) { return y(d['Total']); })
.style("fill-opacity",1);
}
// Legend
var legend = svg.selectAll(".legend")
.data(colorScale.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 22)
.attr("y", 4)
.attr("width", 18)
.attr("height", 18)
.style("fill", colorScale)
.on("click", legendClick);
legend.append("text")
.attr("x", width - 28)
.attr("y", 13)
.attr("dy", ".35em")
.style("text-anchor", "end")
.style("font-size","12px")
.text(function(d) { return d; });
}); // Ends Data Function
</script>
<br />
<p id="disclaimer">
Source: Lotame Data Management Platform, Advance Digital Audiences, July 2015. Exact audience sizes are provided for comparison purposes only and will vary over time and with seasonality.
</p>
</body>
</html>