Skip to content

Instantly share code, notes, and snippets.

@jieqianzhang
Last active April 20, 2019 01:42
Show Gist options
  • Save jieqianzhang/890c4a214e48bedc8755 to your computer and use it in GitHub Desktop.
Save jieqianzhang/890c4a214e48bedc8755 to your computer and use it in GitHub Desktop.
Horserace tool

Built with blockbuilder.org

Horserace tool

This is a tool I built for Google News Lab to compare the Google search indexes of all presidential candidates

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Language" content="en" />
<title>Search Ranks of Presidential Candidates</title>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<link href='http://fonts.googleapis.com/css?family=Abel' rel='stylesheet' type='text/css'>
</head>
<style>
body {
font-size: 12px;
font-family: 'Abel', sans-serif;
margin-top: 20px;
}
li{
margin-left: 0px;
}
.x.axis path,.x.axis line {
fill: none;
stroke: none;
}
.y.axis path {
fill: none;
}
.y.axis line {
fill: none;
stroke: #eeeeee;
shape-rendering: crispEdges;
}
.x.axis text, .y.axis text{
font-size: 10px;
fill:grey;
}
.line {
fill: none;
stroke-width: 2px;
}
.tick line{
stroke: #e0e1e1;
opacity: 1;
}
#title, .explain{
text-indent: 15px;
}
#credit{
color:grey;
text-indent: 20px;
line-height: 30px;
}
a:hover{
cursor:pointer;
}
</style>
<body>
<h1 id="title">Weekly Rankings of Presidential Candidates on Google Search</h1>
<h5 id="credit">By <a href="https://twitter.com/Jieqian_Zhang">@Jieqian_Zhang</a></h5>
<p class="explain"> · Click on a candidate's name in dropdown menus to see the search interest ranking of him/her among all candidates on Google over the past six months.</p>
<p class="explain"> · Hover over a candidate's name tag to see the search interest trend of him/her. </p>
<p class="explain"> · Click on the Replay Button to replay the race.</p>
<div class="bs-example">
<ul class="nav nav-pills" role="tablist">
<li role="presentation" class="dropdown">
<a id="drop4" data-target="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
Democrats
<span class="caret"></span>
</a>
<ul id="menu1" class="dropdown-menu" aria-labelledby="drop4">
<!-- <li id="democrats"><a data-target="#">All Democrats</a></li>
<li role="separator" class="divider"></li> -->
<li class="candidatename"><a data-target="#">Hillary Clinton</a></li>
<li class="candidatename"><a data-target="#">Bernie Sanders</a></li>
<li class="candidatename"><a data-target="#">Martin O'Malley</a></li>
<li class="candidatename"><a data-target="#">Lincoln Chafee</a></li>
<li class="candidatename"><a data-target="#">Jim Webb</a></li>
</ul>
</li>
<li role="presentation" class="dropdown">
<a id="drop5" data-target="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
Republicans
<span class="caret"></span>
</a>
<ul id="menu2" class="dropdown-menu" aria-labelledby="drop5">
<!-- <li id="republicans"><a data-target="#">All Republicans</a></li>
<li role="separator" class="divider"></li> -->
<li class="candidatename"><a data-target="#">Donald Trump</a></li>
<li class="candidatename"><a data-target="#">Jeb Bush</a></li>
<li class="candidatename"><a data-target="#">Rand Paul</a></li>
<li class="candidatename"><a data-target="#">Ben Carson</a></li>
<li class="candidatename"><a data-target="#">Marco Rubio</a></li>
<li class="candidatename"><a data-target="#">Chris Christie</a></li>
<li class="candidatename"><a data-target="#">Lindsey Graham</a></li>
<li class="candidatename"><a data-target="#">Rick Perry</a></li>
<li class="candidatename"><a data-target="#">Ted Cruz</a></li>
<li class="candidatename"><a data-target="#">Carly Fiorina</a></li>
<li class="candidatename"><a data-target="#">Rick Santorum</a></li>
<li class="candidatename"><a data-target="#">Mike Huckabee</a></li>
<li class="candidatename"><a data-target="#">Bobby Jindal</a></li>
<li class="candidatename"><a data-target="#">John Kasich</a></li>
<li class="candidatename"><a data-target="#">George Pataki</a></li>
</ul>
</li>
<li role="presentation" class="active" id="replay"><a data-target="#">Replay Race</a></li>
</ul>
</div>
<div id="graphic"></div>
<script src="http://d3js.org/d3.v3.js"></script>
<script>
function drawGrahpics(){
var margin = {top: 90, right: 120, bottom: 30, left: 50},
width = 1200 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom;
var speed = 500;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(0)
.ticks(25)
.orient("top");
var xtickLabels = ["Dec 28, 2014","Jan 4, 2015","Jan 11, 2015","Jan 18, 2015","Jan 25, 2015","Feb 1,2015","Feb 8, 2015","Feb 15, 2015","Feb 22, 2015","Mar 1, 2015","Mar 8, 2015","Mar 15, 2015","Mar 22, 2015","Mar 29, 2015","Apr 5, 2015","Apr 12, 2015","Apr 19, 2015","Apr 26, 2015","May 3, 2015","May 10, 2015","May 17, 2015","May 24, 2015","May 31, 2015","Jun 7, 2015","Jun 14, 2015"];
xAxis.tickFormat(function(d,i){
return xtickLabels[i]
});
var yAxis = d3.svg.axis()
.scale(y)
.tickFormat(function(d) { return d;})
.ticks(16)
.innerTickSize(- width)
.tickPadding(10)
.outerTickSize(0)
.orient("left");
var line = d3.svg.line()
.x(function(d) { return x(d.week); })
.y(function(d) { return y(d.rank); });
var svg = d3.select("#graphic").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.call(responsivefy)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var clip = svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("x","-5")
.attr("y","-20")
.attr("width", 0)
.attr("height", height*1.2);
d3.csv("rankdata.csv", function(error, data) {
var color = d3.scale.category20();
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "week"; }));
data.forEach(function(d) {
d.week = +[d.week];
});
var candidates = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {name:name, week: d.week, rank: +d[name]};
})
};
});
x.domain(d3.extent(data, function(d) { return d.week; }));
y.domain([
d3.min(candidates, function(c) { return d3.min(c.values, function(v) { return v.rank ; }); }),
d3.max(candidates, function(c) { return d3.max(c.values, function(v) { return v.rank ; }); })
].reverse());
svg.append("g")
.attr("class", "x axis")
.attr("transform","translate(0,0)")
.call(xAxis)
.selectAll("text")
.style("text-anchor","start")
.attr("dx", "2.3em")
.attr("dy", "-0.9em")
.attr("transform",function(d){
return "rotate(-60)"});
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
//timeline
svg.append("line")
.attr(
{
"class":"horizontalGrid",
"x1" : -2,
"x2" : width,
"y1" : y(1) - 13,
"y2" : y(1) - 13,
"fill" : "none",
"shape-rendering" : "crispEdges",
"stroke" : "#e0e1e1",
"stroke-width" : "1px",
"stroke-dasharray": ("3, 3")
})
.attr("id","dotted")
.attr("clip-path", function(d) { return "url(#clip)"; });
//end of timeline
var candidates = svg.selectAll(".candidate")
.data(candidates)
.enter().append("g")
.attr("class", "candidate");
function colorFilter(d){
if (d.name === "Hillary Clinton") {
return "#7183d3";
} else if (d.name === "Bernie Sanders") {
return "#7183d3";
} else if (d.name === "Martin O'Malley"){
return "#7183d3";
} else if (d.name === "Lincoln Chafee"){
return "#7183d3";
} else if (d.name === "Jim Webb"){
return "#7183d3";
}else {
return "#ce3b69";}
}
var path = svg.selectAll(".candidate").append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.attr("clip-path", function(d) { return "url(#clip)"; })
.style("stroke", colorFilter)
var circleStart = candidates.append("circle")
.attr("cx", "0")
.attr("cy", function(d) { return y(d.values[0].rank); })
.style("fill", colorFilter)
.attr("r", 2)
var circleEnd = candidates.append("circle")
.attr("cx", function(d) { return x(d.values[0].week); })
.attr("cy", function(d) { return y(d.values[0].rank);} )
.style("fill", colorFilter)
.attr("r", 2);
var timemark = candidates.append("path")
.attr("d", d3.svg.symbol().type("triangle-up"))
.style("fill", "grey")
.attr("transform",function(d) { return "translate(" + (x(d.values[0].week) ) + "," + (y(1)-15) + ") rotate(-30)"; })
var round = candidates.append("circle")
.attr("transform", function(d) { return "translate(" + (x(d.values[0].week) + 15) + "," + (y(d.values[0].rank)) + ")"; })
.attr("x", 0)
.attr("y",0)
.attr("r", 10)
.on("mouseover", function (d) {
candidates.style("opacity",0.1);
candidates.filter(function(path) {return path.name === d.name; }).style("opacity",1);
})
.on("mouseout", function (d) { candidates.style("opacity",1); })
.style("fill", colorFilter);
var ranking = candidates.append("text")
.attr("transform", function(d) { return "translate(" + (x(d.values[0].week) + 15 ) + "," + (y(d.values[0].rank) ) + ")"; })
.attr("x", 0)
.attr("dy", ".31em")
.attr("text-anchor","middle")
.on("mouseover", function (d) {
candidates.style("opacity",0.1);
candidates.filter(function(path) {return path.name === d.name; }).style("opacity",1);
})
.on("mouseout", function (d) { candidates.style("opacity",1); })
.style("cursor","pointer")
.style("fill", "#ffffff")
.style("font-weight", "bold")
.text(function(d) { return d.values[0].rank; });
var label = candidates.append("text")
.attr("transform", function(d) { return "translate(" + (x(d.values[0].week) + 20) + "," + (y(d.values[0].rank) ) + ")"; })
.attr("x", 8)
.attr("dy", ".31em")
.attr("id","label")
.on("mouseover", function (d) {
candidates.style("opacity",0.1);
candidates.filter(function(path) {return path.name === d.name; }).style("opacity",1);
})
.on("mouseout", function (d) { candidates.style("opacity",1); })
.style("cursor","pointer")
.style("stroke", colorFilter)
.text(function(d) { return d.name; });
var week = 1;
var transition = d3.transition()
.delay(500)
.duration(speed)
.each("start", function start() {
label.transition()
.duration(speed)
.ease('linear')
.attr("transform", function(d) { return "translate(" + (x(d.values[week].week) + 20) + "," + (y(d.values[week].rank)) + ")"; })
.text(function(d) { return d.name; });
ranking.transition()
.duration(speed)
.ease('linear')
.attr("transform", function(d) { return "translate(" + (x(d.values[week].week) + 15) + "," + (y(d.values[week].rank) ) + ")"; })
.text(function(d,i) { return d.values[week].rank; });
round.transition()
.duration(speed)
.ease('linear')
.attr("transform", function(d) { return "translate(" + (x(d.values[week].week) + 15) + "," + (y(d.values[week].rank)) + ")"; });
circleEnd.transition()
.duration(speed)
.ease('linear')
.attr("cx", function(d) { return x(d.values[week].week); })
.attr("cy", function(d) { return y(d.values[week].rank); });
clip.transition()
.duration(speed)
.ease('linear')
.attr("width", x(week+1)+5)
.attr("height", height*1.2);
timemark.transition()
.duration(speed)
.ease('linear')
.attr("transform",function(d) { return "translate(" + (x(d.values[week].week) ) + "," + (y(1)-15) + ") rotate(-30)"; })
week+=1;
if (week !== data[0].length){
transition = transition.transition().each("start", start);}
});
$(".candidatename").on("click",function(){
var nameOfCandidate = $(this).text();
candidates.style("opacity",0.1);
candidates.filter(function(path) {
return path.name === nameOfCandidate;
}).style("opacity",1);
});
});
function responsivefy(svg) {
var container = d3.select(svg.node().parentNode),
width = parseInt(svg.style("width")),
height = parseInt(svg.style("height")),
aspect = width / height;
svg.attr("viewBox", "0 0 " + width + " " + height)
.attr("perserveAspectRatio", "xMinYMid")
.call(resize);
d3.select(window).on("resize." + container.attr("#graphic"), resize);
function resize() {
var targetWidth = parseInt(container.style("width"));
svg.attr("width", targetWidth * 0.8);
svg.attr("height", Math.round(targetWidth /aspect * 0.8));
}
}
}
</script>
</body>
<script>
drawGrahpics()
d3.select("#replay")
.on("click", function(){
location.reload()
})
</script>
</html>
week Jeb Bush Hillary Clinton Donald Trump Ben Carson Ted Cruz Chris Christie Rand Paul Marco Rubio Scott Walker Bernie Sanders Rick Santorum Jim Webb Rick Perry George Pataki Carly Fiorina Mike Huckabee John Kasich Martin O'Malley Bobby Jindal Lindsey Graham Lincoln Chafee
1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
2 4 3 2 6 8 1 7 11 9 13 12 15 10 19 14 5 17 20 18 16 21
3 5 1 2 6 4 3 9 7 10 12 14 19 11 18 13 8 16 21 15 17 20
4 7 2 1 6 4 11 9 8 10 14 15 18 12 20 17 3 16 19 5 13 21
5 4 2 1 5 7 6 9 10 3 13 15 18 11 19 17 8 16 20 12 14 21
6 6 2 4 7 10 5 3 11 1 14 17 19 13 16 12 8 18 20 9 15 21
7 5 1 4 3 8 7 6 13 2 11 10 18 14 19 17 12 16 20 9 15 21
8 4 1 2 5 8 7 6 10 3 11 15 18 12 19 13 9 16 20 14 17 21
9 4 2 3 5 8 7 6 10 1 14 16 15 12 19 9 13 18 20 11 17 21
10 5 1 4 2 8 7 6 9 3 11 15 18 12 20 14 10 17 19 16 13 21
11 5 1 2 3 8 9 6 10 4 11 16 14 15 20 13 12 19 18 17 7 21
12 6 1 2 5 3 8 7 9 4 12 11 15 17 20 14 10 18 19 13 16 21
13 5 2 3 7 1 9 4 8 6 10 11 16 15 20 17 13 14 19 12 18 21
14 7 1 3 8 2 10 9 6 5 11 13 19 14 20 4 12 17 18 15 16 21
15 4 2 6 7 3 9 1 8 5 11 12 18 15 21 10 14 19 20 16 17 13
16 5 1 6 7 4 8 3 2 9 11 18 15 16 21 10 12 20 17 14 19 13
17 6 1 7 9 4 8 3 2 5 12 10 17 18 21 11 16 15 19 13 14 20
18 6 2 3 8 4 9 5 7 10 1 13 15 18 21 11 12 16 14 17 19 20
19 9 2 6 1 7 12 8 11 10 3 13 16 14 21 4 5 17 19 18 15 20
20 2 1 7 4 6 9 8 5 11 3 14 17 13 16 10 12 18 21 15 19 20
21 4 1 7 5 6 12 3 8 10 2 15 19 16 18 14 9 17 20 13 11 21
22 9 1 7 5 8 14 4 11 13 2 3 20 18 6 12 10 19 16 15 17 21
23 9 1 8 10 6 7 3 11 12 2 14 21 5 19 16 13 20 17 18 4 15
24 4 1 7 9 10 6 8 3 11 2 14 20 12 19 13 15 17 21 18 5 16
25 2 3 1 6 12 8 5 7 11 4 14 21 10 19 13 16 18 15 17 9 20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment