block by enjalot 7149418

html5devconf intermediate d3.js workshop

Full Screen

The end goal project for the html5devconf intermediate d3.js workshop

extra credit: use d3.tip

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<html>
  <head>
    <style>
      svg {
        width: 500px;
        height: 500px;
        border: 1px solid gray;
        float:left;
      }
      .table {
        width: 400px;
        height: 500px;
        overflow: scroll;
        float:left;
      }
      .axis {
        font-size: 10px;
      }
      .axis path {
        fill: none;
        stroke: #000000;
      }
      .axis .tick line {
        stroke: #000;
      }

      td {
        padding: 5px;
      }
    </style>
  </head>
  <body>
   
    <svg></svg>
    <div id="display"></div>

    <script src="//d3js.org/d3.v3.js"></script>
    <script src="table.js"></script>
    <script src="scatter.js"></script>
    <script src="brush.js"></script>
    <script>
      var display = d3.select("#display");

      d3.json("pics.json", function(err, pics) {
        var data = pics.data.children;
        data.forEach(function(d) {
          d.data.created *= 1000;
        })
        console.log(data);

        var display = d3.select("#display")

        //table
        var tdiv = display.append("div").classed("table", true)
        var table = d3.chart.table()
        table.data(data)
        table(tdiv);

        var svg = d3.select("svg")
        //scatter plot
        var sgroup = svg.append("g")
          .attr("transform", "translate(50, 0)")
        var scatter = d3.chart.scatter()
        scatter.data(data)
        scatter.width(400)
        scatter(sgroup)


        //brush
        var bgroup = svg.append("g")
          .attr("transform", "translate(50, 430)")
        var brush = d3.chart.brush()
        brush
          .data(data)
          .width(400)
        brush(bgroup)

        brush.on("filter", function(filtered) {
          console.log("filtered", filtered);
          scatter.data(filtered);
          scatter.update();
          table.data(filtered)
          table.update();
        })

        table.on("hover", function(hovered) {
          scatter.highlight(hovered)
          brush.highlight(hovered)
        })
        scatter.on("hover", function(hovered) {
          table.highlight(hovered)
          brush.highlight(hovered)
        })
      })

    </script>
  </body>
</html>

brush.js

if(!d3.chart) d3.chart = {};

d3.chart.brush = function() {
  var g;
  var data;
  var width = 600;
  var height = 30;
  var dispatch = d3.dispatch(chart, "filter");

  function chart(container) {
    g = container;

    var extent = d3.extent(data, function(d) {
      return d.data.created
    })

    var scale = d3.time.scale()
      .domain(extent)
      .range([0, width])

    var brush = d3.svg.brush()
    brush.x(scale)
    brush(g)
    g.selectAll("rect").attr("height", height)
    g.selectAll(".background")
      .style({fill: "#4B9E9E", visibility: "visible"})
    g.selectAll(".extent")
      .style({fill: "#78C5C5", visibility: "visible"})
    g.selectAll(".resize rect")
      .style({fill: "#276C86", visibility: "visible"})

    var rects = g.selectAll("rect.events")
    .data(data)
    rects.enter()
    .append("rect").classed("events", true)
    rects.attr({
      x: function(d) { return scale(d.data.created);},
      y: 0,
      width: 1,
      height: height
    }).style("pointer-events", "none")

    rects.exit().remove()

    brush.on("brushend", function() {
      var ext = brush.extent()
      var filtered = data.filter(function(d) {
        return (d.data.created > ext[0] && d.data.created < ext[1])
      })
      g.selectAll("rect.events")
      .style("stroke", "")

      g.selectAll("rect.events")
      .data(filtered, function(d) { return d.data.id })
      .style({
        stroke: "#fff"
      })

      //emit filtered data
      dispatch.filter(filtered)
    })

    var axis = d3.svg.axis()
    .scale(scale)
    .orient("bottom")
    .tickValues([new Date(extent[0]), new Date(extent[0] + (extent[1] - extent[0])/2) , new Date(extent[1])])
    .tickFormat(d3.time.format("%b %d %H:%M"))

    var agroup = g.append("g")
    agroup.attr("transform", "translate(" + [0, height] + ")")
    axis(agroup)
    agroup.selectAll("path")
      .style({ fill: "none", stroke: "#000"})
    agroup.selectAll("line")
      .style({ stroke: "#000"})
  }

  chart.highlight = function(data) {
    var rects = g.selectAll("rect.events")
    .style("stroke", "")
    .style("stroke-width", "")

    rects.data(data, function(d) { return d.data.id })
    .style("stroke", "orange")
    .style("stroke-width", 3)
  }

  chart.data = function(value) {
    if(!arguments.length) return data;
    data = value;
    return chart;
  }
  chart.width = function(value) {
    if(!arguments.length) return width;
    width = value;
    return chart;
  }
  chart.height = function(value) {
    if(!arguments.length) return height;
    height = value;
    return chart;
  }

  return d3.rebind(chart, dispatch, "on");
}

scatter.js

if(!d3.chart) d3.chart = {};

d3.chart.scatter = function() {
  var g;
  var data;
  var width = 400;
  var height = 400;
  var cx = 10;
  var numberBins = 5;
  var dispatch = d3.dispatch(chart, "hover");

  function chart(container) {
    g = container;

    g.append("g")
    .classed("xaxis", true)

    g.append("g")
    .classed("yaxis", true)

    update();
  }
  chart.update = update;
  function update() {
    var maxCreated = d3.max(data, function(d) { return d.data.created });
    var minCreated = d3.min(data, function(d) { return d.data.created });
    var maxScore = d3.max(data, function(d) { return d.data.score })

    var colorScale = d3.scale.category20();
    var createdScale = d3.time.scale()
        .domain([minCreated, maxCreated])
        .range([cx, width])

    var commentScale = d3.scale.linear()
    .domain(d3.extent(data, function(d) { return d.data.num_comments }))
    .range([3, 15])

    var yScale = d3.scale.linear()
      .domain([0, maxScore])
      .range([height, cx])

     var xAxis = d3.svg.axis()
    .scale(createdScale)
    .ticks(3)
    .tickFormat(d3.time.format("%b %d %H:%M"))

    var yAxis = d3.svg.axis()
    .scale(yScale)
    .ticks(3)
    .orient("left")

    var xg = g.select(".xaxis")
      .classed("axis", true)
      .attr("transform", "translate(" + [0,height] + ")")
      .transition()
      .call(xAxis)

    var yg = g.select(".yaxis")
      .classed("axis", true)
      .classed("yaxis", true)
      .attr("transform", "translate(" + [cx - 5,0] + ")")
      .transition()
      .call(yAxis)

    var circles = g.selectAll("circle")
    .data(data, function(d) { return d.data.id })

    circles.enter()
    .append("circle")

    circles
    .style({
      fill: function(d,i) { return colorScale(i) }
    })
    .transition()
    .attr({
      cx: function(d,i) { return createdScale(d.data.created) },
      cy: function(d,i) { return yScale(d.data.score) },
      r: function(d) { return commentScale(d.data.num_comments)}
    })

    circles.exit().remove()

    circles.on("mouseover", function(d) {
      d3.select(this).style("stroke", "black")
      dispatch.hover([d])
    })
    circles.on("mouseout", function(d) {
      d3.select(this).style("stroke", "")
      dispatch.hover([])
    })
  }

  chart.highlight = function(data) {
    var circles = g.selectAll("circle")
    .style("stroke", "")

    circles.data(data, function(d) { return d.data.id })
    .style("stroke", "orange")
    .style("stroke-width", 3)
  }

  chart.data = function(value) {
    if(!arguments.length) return data;
    data = value;
    return chart;
  }
  chart.width = function(value) {
    if(!arguments.length) return width;
    width = value;
    return chart;
  }
  chart.height = function(value) {
    if(!arguments.length) return height;
    height = value;
    return chart;
  }

  return d3.rebind(chart, dispatch, "on");
}

table.js

if(!d3.chart) d3.chart = {};

d3.chart.table = function() {
  var div;
  var data;
  var width;
  var dispatch = d3.dispatch(chart, "hover");
  function chart(container) {
    div = container;

    var table = container.append("table")
    update();
  }
  chart.update = update;

  function update() {
    var table = div.select("table")
    var rows = table.selectAll("tr.row")
    .data(data, function(d) { return d.data.id })

    console.log("table data", data)

    rows.exit().remove();
    var rowsEnter = rows.enter()
    .append("tr").classed("row", true)

    rowsEnter.append("td")
    .text(function(d) { return d.data.score })

    rowsEnter.append("td")
    .append("a")
    .attr({
      href: function(d) { return d.data.url }
    })
    .append("img")
    .attr({
      src: function(d) { return d.data.thumbnail }
    })

    rowsEnter.append("td")
    .append("a")
    .attr({
      href: function(d) { return d.data.url }
    }).text(function(d) { return d.data.title })

    rowsEnter.append("td")
    .text(function(d) { return d.data.ups })

    rowsEnter.append("td")
    .text(function(d) { return d.data.downs })

    rowsEnter.on("mouseover", function(d) {
      d3.select(this).style("background-color", "orange")
      dispatch.hover([d])
    })
    rowsEnter.on("mouseout", function(d) {
      d3.select(this).style("background-color", "")
      dispatch.hover([])
    })
  }

  chart.highlight = function(data) {
    var trs = div.selectAll("tr")
    .style("background-color", "")

    trs.data(data, function(d) { return d.data.id })
    .style("background-color", "orange")
  }

  chart.data = function(value) {
    if(!arguments.length) return data;
    data = value;
    return chart;
  }
  chart.width = function(value) {
    if(!arguments.length) return width;
    width = value;
    return chart;
  }

  return d3.rebind(chart, dispatch, "on");
}