|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<html> |
|
<head> |
|
<title>Where are the missing Democrats?</title> |
|
<link rel = "stylesheet" type = "text/css" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"> |
|
|
|
<style type = "text/css"> |
|
body { |
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; |
|
position: relative; |
|
} |
|
|
|
#container { |
|
padding: 10px; |
|
width: 960px; |
|
} |
|
|
|
svg { background:rgb(255,255,255);position:relative;} |
|
svg:not(:root) { overflow: hidden; } |
|
|
|
.axis text { |
|
font: 10px sans-serif; |
|
} |
|
|
|
.axis path, |
|
.axis line { |
|
fill: none; |
|
stroke: #333; |
|
|
|
shape-rendering: crispEdges; |
|
} |
|
|
|
|
|
|
|
#tt { pointer-events: none;color:white;} |
|
|
|
#tipContainer { font-size:16px;position:absolute;width:180px;z-index:100;background-repeat:no-repeat;text-align:left;line-height:20px;} |
|
|
|
#tipLocation {font-weight:normal;font-family:Georgia; font-style: Italic; color:white;margin:0px;padding:10px 10px;background:#333;font-size:14px;} |
|
|
|
#tipCount {font-weight:bold;font-size:32px;letter-spacing:-1px;margin:0px;padding:0px 10px 10px 10px;color:#333;} |
|
|
|
#tipKey {font-weight:normal;font-size:10px;color:#333;margin:0px;padding:5px 0px 5px 10px;background:#eee;} |
|
|
|
.tipClear { clear:both;} |
|
|
|
|
|
</style> |
|
|
|
</head> |
|
|
|
<body> |
|
<h3 style = "font-size: 18px; padding: 10px;"><i>Under- and over-representation of Democrats in the U.S. House, 113th Congress, by state</i></h3> |
|
<p style = "padding-left: 10px;"><b>Bars are colored according to which party is in control of the state government: red for Republicans, blue for Democrats, grey for split/other. Mouse over for detail.</b></p> |
|
<p style = "padding-left: 10px;">The y-axis indicates the difference between the number of actual and expected Democratic seats in a state's House delegation. Expected values are based on total votes for a given party across all House races in a given state. Negative values indicate fewer Democratic seats than expected, positive indicate more Democratic seats than expected.<p> |
|
|
|
|
|
<div id = "container"> |
|
|
|
</div> |
|
|
|
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script> |
|
|
|
|
|
<script> |
|
|
|
|
|
var margin = { |
|
top: 10, |
|
right: 20, |
|
bottom: 20, |
|
left: 40 |
|
}, |
|
w = 960 - margin.left - margin.right, |
|
h = 500 - margin.top - margin.bottom; |
|
|
|
var formatpct = d3.format(".1%"); |
|
var format1 = d3.format(",0f"); |
|
|
|
var x = d3.scale.ordinal().rangeRoundBands([0, w], .1, 1); |
|
|
|
var y = d3.scale.linear().range([h, 0]); |
|
|
|
var red = "#bb1813", |
|
blue = "#0071bc"; |
|
|
|
var xAxis = d3.svg.axis().scale(x).tickSize(5).orient("bottom"); |
|
|
|
var yAxis = d3.svg.axis().scale(y).orient("left"); |
|
|
|
var svg = d3.select("#container").append("svg").attr("width", w + margin.left + margin.right).attr("height", h + margin.top + margin.bottom).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
|
|
|
var tooltip = d3.select("#container").append("div").attr("id", "tt").style("z-index", "10").style("position", "absolute").style("visibility", "hidden"); |
|
|
|
|
|
d3.csv("results.csv", function (error, data) { |
|
data.forEach(function (d) { |
|
for (var prop in d) { |
|
if (!isNaN(d[prop])) { |
|
d[prop] = +d[prop] |
|
}; |
|
} |
|
|
|
}); |
|
|
|
x.domain(data.sort(function (a, b) { |
|
return b.diffdem - a.diffdem; |
|
}).map(function (d) { |
|
return d.state; |
|
})) |
|
y.domain([-5, 5]); |
|
|
|
svg.append("g").attr("class", "y axis").call(yAxis).attr("transform", "translate(-10," + 0 + ")").append("text").attr("class", "ylabel").attr("transform", "rotate(-90)") |
|
|
|
.attr("y", 6).attr("dy", ".71em").style("text-anchor", "end").text("Diff. between actual and expected Democratic seats in state's House delegation"); |
|
|
|
svg.selectAll(".bar").data(data).enter().append("rect").attr("class", ".bar").attr("x", function (d) { |
|
return x(d.state); |
|
}).attr("width", x.rangeBand()).attr("y", function (d) { |
|
return y(Math.max(0, -d.diffdem)); |
|
}).attr("height", function (d) { |
|
return Math.abs(y(-d.diffdem) - y(0)); |
|
}).style("fill", function (d) { |
|
if (d.legis_control == "Dem") { |
|
return blue; |
|
} else if (d.legis_control == "Rep") { |
|
return red; |
|
} else { |
|
return "#ccc"; |
|
} |
|
}).style("fill-opacity", 0.6).on("mouseover", function (d) { |
|
return toolOver(d, this) |
|
}).on("mouseout", function (d) { |
|
return toolOut(d, this) |
|
}).on("mousemove", function (d, i) { |
|
myPos = d3.mouse(this); |
|
myX = myPos[0]; |
|
myY = myPos[1]; |
|
return toolMove(d.state, d.totaldr, d.totalhouse, d.dem, d.expdemseats, d.demhouse, d.diffdem) |
|
}); |
|
|
|
|
|
svg.append("g").attr("class", "x axis").attr("transform", "translate(0," + (h / 2) + ")").style("pointer-events", "none").call(xAxis).append("text").attr("class", "xlabel").attr("x", w).attr("y", -6).style("text-anchor", "end").text("State"); |
|
|
|
svg.append("text").attr("id", "underlabel").style({ |
|
"font-size": "18px", |
|
"font-weight": "bold", |
|
"fill": "#333" |
|
}).attr("x", 25).attr("y", h / 2 - 100).text("Total under-representation:"); |
|
|
|
svg.append("text").attr("id", "underval").style({ |
|
"font-size": "60px", |
|
"font-weight": "bold", |
|
"fill": "#333" |
|
}).attr("x", 25).attr("y", h / 2 - 40).text("40.79 seats"); |
|
|
|
svg.append("text").attr("id", "overlabel").style({ |
|
"font-size": "18px", |
|
"font-weight": "bold", |
|
"fill": "#333", |
|
"text-anchor": "end" |
|
}).attr("x", w - 25).attr("y", h / 2 + 50).text("Total over-representation:"); |
|
|
|
svg.append("text").attr("id", "overval").style({ |
|
"font-size": "60px", |
|
"font-weight": "bold", |
|
"fill": "#333", |
|
"text-anchor": "end" |
|
}).attr("x", w - 25).attr("y", h / 2 + 110).text("22.79 seats"); |
|
|
|
|
|
svg.append("text").attr("id", "overlabel2").style({ |
|
"font-size": "18px", |
|
"font-weight": "bold", |
|
"fill": "#333", |
|
"text-anchor": "end" |
|
}).attr("x", w - 25).attr("y", h / 2 + 160).text("Net difference, under- vs. over-representation:"); |
|
|
|
svg.append("text").attr("id", "overval2").style({ |
|
"font-size": "60px", |
|
"font-weight": "bold", |
|
"fill": "#333", |
|
"text-anchor": "end" |
|
}).attr("x", w - 25).attr("y", h / 2 + 220).text("18 seats"); |
|
|
|
}); |
|
|
|
|
|
function toolOver(v, thepath) { |
|
|
|
d3.select(thepath).style({ |
|
"fill-opacity": "1" |
|
}); |
|
return tooltip.style("visibility", "visible"); |
|
}; |
|
|
|
function toolOut(m, thepath) { |
|
d3.select(thepath).style({ |
|
"fill-opacity": "0.6" |
|
}); |
|
return tooltip.style("visibility", "hidden"); |
|
}; |
|
|
|
|
|
function toolMove(state, totalvotes, totalreps, demvotes, expecteddems, actualdems, diff) { |
|
if (myX < 120) { |
|
myX = 120 |
|
}; |
|
return tooltip.style("top", myY + -30 + "px").style("left", myX - 120 + "px").html("<div id='tipContainer'><div id='tipLocation'><b>" + state + "</b></div><div id='tipKey'>Total votes: <b>" + format1(totalvotes) + "</b><br>Dem votes: <b>" + format1(demvotes) + "</b><br>Total reps from state: <b>" + totalreps + "</b><br>Expected Dem seats: <b>" + expecteddems + "</b><br>Actual Dem seats: <b>" + actualdems + "</b><br>Difference: <b>" + -diff + "</b></div><div class='tipClear'></div> </div>"); |
|
}; |
|
d3.select(self.frameElement).style("height", "700px"); |
|
|
|
</script> |
|
|
|
|
|
</body> |
|
</html> |