D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
duncangeere
Full window
Github gist
Newsletter Statistics
Built with
blockbuilder.org
<!DOCTYPE html> <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>