Built with blockbuilder.org
xxxxxxxxxx
<html lang="en">
<head>
<meta charset="utf-8">
<title>Newsletter Statistics</title>
<script type="text/javascript" src="d3.js"></script>
<style type="text/css">
/* No style rules here yet */
</style>
</head>
<body>
<script type="text/javascript">
//Width and height
var w = 800;
var h = 500;
var padding = 40;
var dataset, xScale, yScale, xAxis, yAxis; //Empty, for now
//For converting strings to Dates
var parseTime = d3.timeParse("%d/%m/%Y");
//For converting Dates to strings
var formatTime = d3.timeFormat("%b %d");
//Function for converting CSV values from strings to Dates and numbers
var rowConverter = function(d) {
return {
Date: parseTime(d.Date),
Title: d.Title,
Recipients: parseInt(d.Recipients),
Opens: parseInt(d.Opens),
OpenPerc: parseInt(d.OpenPerc),
Clicks: parseInt(d.Clicks),
ClickPerc: parseInt(d.ClickPerc),
};
}
//Load in the data
d3.csv("newsletter.csv", rowConverter, function(data) {
//Copy data into global dataset
dataset = data;
//Discover start and end dates in dataset
var startDate = d3.min(dataset, function(d) { return d.Date; });
var endDate = d3.max(dataset, function(d) { return d.Date; });
//Create scale functions
xScale = d3.scaleTime()
.domain([
d3.timeDay.offset(startDate, -0), //startDate minus one day, for padding
d3.timeDay.offset(endDate, 0) //endDate plus one day, for padding
])
.range([padding, w - padding])
.nice();
yScale = d3.scaleLinear()
.domain([
0, //Because I want a zero baseline
d3.max(dataset, function(d) { return d.Recipients; })
])
.range([h - padding, padding])
.nice();
//Define X axis
xAxis = d3.axisBottom()
.scale(xScale)
.ticks(7)
.tickFormat(formatTime);
//Define Y axis
yAxis = d3.axisLeft()
.scale(yScale);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Generate guide lines
svg.selectAll("line")
.data(dataset)
.enter()
.append("line")
.attr("x1", function(d) {
return xScale(d.Date);
})
.attr("x2", function(d) {
return xScale(d.Date);
})
.attr("y1", h - padding)
.attr("y2", function(d) {
return yScale(d.Recipients);
})
.attr("stroke", "#ddd")
.attr("stroke-width", 1);
//Generate circles next, so they appear in front of the lines
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d.Date);
})
.attr("cy", function(d) {
return yScale(d.Recipients);
})
.attr("r", 2);
//Add labels
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return(d.Title+" "+d.Recipients);})
.attr("font-family","sans-serif")
.attr("font-size","11px")
.attr("fill","black")
.attr("text-anchor","start")
.attr("transform", function(d){
return "translate("+xScale(d.Date)+","+yScale(d.Recipients)+") rotate(90)"})
.attr("dx","8")
.attr("dy","3.5");
//Create X axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
//Create Y axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
//Title
svg.append("text")
.attr("class","title")
.attr("transform","translate("+(padding+10)+","+(padding+5)+")")
.text("Duncan's Newsletter statistics over time")
.attr("font-family","sans-serif")
.attr("font-size","11px");
//Recipients
svg.append("text")
.attr("class","recipients")
.attr("transform","translate("+(padding+38)+","+(padding+20)+")")
.text("Recipients")
.attr("font-family","sans-serif")
.attr("font-size","11px")
.attr("font-weight", "bold")
.attr("text-anchor","middle");
//Opens
svg.append("text")
.attr("class","opens")
.attr("transform","translate("+(padding+93)+","+(padding+20)+")")
.text("Opens")
.attr("font-family","sans-serif")
.attr("font-size","11px")
.attr("font-weight","normal")
.attr("text-anchor","middle");
//Clicks
svg.append("text")
.attr("class","clicks")
.attr("transform","translate("+(padding+135)+","+(padding+20)+")")
.text("Clicks")
.attr("font-family","sans-serif")
.attr("font-size","11px")
.attr("font-weight","normal")
.attr("text-anchor","middle");
// On clicking Opens, update with new data
svg.select(".opens")
.on("click", function() {
svg.select(".recipients")
.attr("font-weight","normal");
svg.select(".opens")
.attr("font-weight","bold");
svg.select(".clicks")
.attr("font-weight","normal");
/*
// Update yScale
yScale.domain([0, d3.max(dataset, function(d) {return d.Opens;})])
.nice();
*/
// Update lines
svg.selectAll("line")
.data(dataset)
.transition()
.duration(1000)
.attr("y2", function(d) {
return yScale(d.Opens);
})
// Update circles
svg.selectAll("circle")
.data(dataset)
.transition()
.duration(1000)
.attr("cy", function(d) {
return yScale(d.Opens);
})
// Update labels
svg.selectAll("text")
.data(dataset)
.transition()
.duration(1000)
.text(function(d) {
return(d.Title+" "+d.Opens);})
.attr("text-anchor","start")
.attr("transform", function(d){
return "translate("+xScale(d.Date)+","+yScale(d.Opens)+") rotate(90)"})
.attr("dx","8")
//Update Y axis
svg.select(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
});
// On clicking Clicks, update with new data
svg.select(".clicks")
.on("click", function() {
svg.select(".recipients")
.attr("font-weight","normal");
svg.select(".opens")
.attr("font-weight","normal");
svg.select(".clicks")
.attr("font-weight","bold");
/*
// Update yScale
yScale.domain([0, d3.max(dataset, function(d) {return d.Clicks;})])
.nice();
*/
// Update lines
svg.selectAll("line")
.data(dataset)
.transition()
.duration(1000)
.attr("y2", function(d) {
return yScale(d.Clicks);
})
// Update circles
svg.selectAll("circle")
.data(dataset)
.transition()
.duration(1000)
.attr("cy", function(d) {
return yScale(d.Clicks);
})
// Update labels
svg.selectAll("text")
.data(dataset)
.transition()
.duration(1000)
.text(function(d) {
return(d.Title+" "+d.Clicks);})
.attr("text-anchor","end")
.attr("transform", function(d){
return "translate("+xScale(d.Date)+","+yScale(d.Clicks)+") rotate(90)"})
.attr("dx","-8")
//Update Y axis
svg.select(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
});
// On clicking Recipients, update with new data
svg.select(".recipients")
.on("click", function() {
svg.select(".recipients")
.attr("font-weight","bold");
svg.select(".opens")
.attr("font-weight","normal");
svg.select(".clicks")
.attr("font-weight","normal");
/*
// Update yScale
yScale.domain([0, d3.max(dataset, function(d) {return d.Recipients;})])
.nice();
*/
// Update lines
svg.selectAll("line")
.data(dataset)
.transition()
.duration(1000)
.attr("y2", function(d) {
return yScale(d.Recipients);
})
// Update circles
svg.selectAll("circle")
.data(dataset)
.transition()
.duration(1000)
.attr("cy", function(d) {
return yScale(d.Recipients);
})
// Update labels
svg.selectAll("text")
.data(dataset)
.transition()
.duration(1000)
.text(function(d) {
return(d.Title+" "+d.Recipients);})
.attr("text-anchor","start")
.attr("transform", function(d){
return "translate("+xScale(d.Date)+","+yScale(d.Recipients)+") rotate(90)"})
.attr("dx","8")
//Update Y axis
svg.select(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
});
});
</script>
</body>
</html>