block by palewire 359f60d94de3a65bbd678837fa4cc9c0

Responsive d3 treemap

Full Screen

This treemap shows the salaries of the Opening Day rosters of the 2017 Houston Astros and Los Angeles Dodgers baseball teams, according to Cot’s Baseball Contracts.

The code uses d3 version 3. Open the preview above in a new window to test responsiveness. This code was developed by Ben Welsh and Ryan Menezes of the Los Angeles Times for the Dec. 14, 2017 story “Why Wall Street gets a cut of your power bill.”.

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.container {
    max-width: 1270px;
    margin: 0 auto;
}
.graphic {
    max-width: 1000px;
    display: block;
    margin: 25px auto;
}
.infobox {
    display: none;
}
</style>
<body>
    <article class="container">
        <section class="graphic">
            <section class="treemap"></section>
            <section class="infobox"></section>
        </section>
    </article>
</body>
<script src="//d1qqc1e9kvmdh8.cloudfront.net/js/jquery-1.12.4/jquery.min.js"></script>
<script src="//d1qqc1e9kvmdh8.cloudfront.net/js/underscore-1.8.3-min.js"></script>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/queue.v1.min.js"></script>
<script>
var app = {};
app.selector = "section.graphic";
app.$el = $(app.selector);
app.$infobox = $(".infobox", app.selector);

d3.selection.prototype.moveToFront = function() {
    return this.each(function(){
        this.parentNode.appendChild(this);
    });
};

app.prep_csv_row = function (row) {
    return {
        name: row.name,
        last_name: row.last_name,
        salary: +row.salary,
        team: row.team
    }
};

app.intcomma = function(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};

app.draw = function () {
    console.log("draw");
    // Calculate the height and width
    app.width = app.$el.width();
    // ... once we're below a mobile cutoff width, the treemap becomes a square
    if (app.width < 580) {
        app.isMobile = true;
        app.scaledHeight = app.width * 1;
    // otherwise it's more of a rectangle
    } else {
        app.isMobile = false;
        app.scaledHeight = app.width * 0.45;
    }
    // Create the d3 layout
    app.chart = d3.layout.treemap()
      .size([app.width, app.scaledHeight])
      .sticky(true)
      .sort(function(x, y){
         return d3.ascending(x.salary, y.salary);
      })
      .value(function(d) { return d.salary; });
    // Create the svg
    app.svg = d3.select("section.treemap")
      .append("svg")
      .attr("width", app.width)
      .attr("height", app.scaledHeight);
    // Create g elements to group all the squares with their labels
    app.nodes = app.svg.datum(app.clustered_data).selectAll("g")
      .data(app.chart.nodes)
      .enter()
        .append("g")
        .attr("data-name", function(d) { return d.name; })
        .attr("data-last-name", function(d) { return d.last_name; })
        .attr("data-team", function (d) { return d.team; })
        .attr("data-salary", function(d) { return d.salary; });
    // Create the squares inside those
    app.nodes.append("rect")
        .attr("x", function(d) { return d.x; })
        .attr("y", function(d) { return d.y; })
        .attr("width", function(d) { return d.dx; })
        .attr("height", function(d) { return d.dy; })
        .attr("stroke", "white")
        .attr("stroke-width", 0.5)
        .attr("fill", function (d) {
            return d.team == 'Dodgers' ? '#005A9C': '#EB6E1F';
        });
    // Add the labels as well
    app.nodes.append('text')
        .attr("x", function(d) { return d.x + 5; })
        .attr("y", function(d) { return d.y + 20; })
        .text(function (d) {
            // Only if there are more than 5 million, to avoid labels on very small boxes
            if ((!app.isMobile) & d.salary > 5000000) {
                return d.last_name;
            } else if (d.salary > 10000000) {
                return d.last_name
            }
        })
        .attr("font-family", "Courier")
        .attr("font-size", function (d) { return app.isMobile == true ? '13px': '16px'; })
        .attr("text-anchor", "left");

    app.nodes.on("mouseover", app.mouseover);
    app.nodes.on("mouseout", app.mouseout);

};

app.resize = function () {
    console.log("resize");
    $("section.treemap").empty();
    app.draw();
};

app.mouseover = function() {
    d3.select(this)
      .select("rect")
      .attr("stroke-width", 2);
    d3.select(this).moveToFront();
    var that = $(this);
    var html = "<b>" + that.attr("data-name") + "</b> $" + app.intcomma(that.attr("data-salary"));
    app.$infobox.html(html).show();
};

app.mouseout = function () {
    d3.select(this)
      .select("rect")
      .attr("stroke-width", 0.3);
    app.$infobox.html("").hide();
}

app.boot = function (error, data) {
    console.log("boot");
    app.data = _.map(data, app.prep_csv_row);
    app.clustered_data = {
      "name": "cluster",
      "children": [
          {
              "name": "dodgers",
              "children":_.where(app.data, {"team": "Dodgers"})
          },
          {
              "name": "astros",
              "children":_.where(app.data, {"team": "Astros"})
          },
      ]
    };
    app.draw();
    d3.select(window).on("resize", app.resize);
};

queue()
   .defer(d3.csv, 'players.csv')
   .await(app.boot);

</script>

players.csv

name,last_name,salary,team
"McCann, Brian",McCann,17000000,Astros
"Beltran, Carlos",Beltran,16000000,Astros
"Gurriel, Yulieski",Gurriel,14400000,Astros
"Reddick, Josh",Reddick,13000000,Astros
"Keuchel, Dallas",Keuchel,9150000,Astros
"Morton, Charlie",Morton,7000000,Astros
"Gregerson, Luke",Gregerson,6250000,Astros
"Sipp, Tony",Sipp,6000000,Astros
"Aoki, Nori",Aoki,5500000,Astros
"Gattis, Evan",Gattis,5200000,Astros
"Altuve, Jose",Altuve,4687500,Astros
"Springer, George",Springer,3900000,Astros
"McHugh, Collin",McHugh,3850000,Astros
"Gonzalez, Marwin",Gonzalez,3725000,Astros
"Fiers, Michael",Fiers,3450000,Astros
"Harris, Will",Harris,2200000,Astros
"Marisnick, Jake",Marisnick,1100000,Astros
"Devenski, Chris",Devenski,554500,Astros
"Giles, Ken",Giles,550100,Astros
"McCullers Jr., Lance",McCullers Jr.,548000,Astros
"Feliz, Michael",Feliz,546200,Astros
"Musgrove, Joe",Musgrove,543400,Astros
"Peacock, Brad",Peacock,541500,Astros
"Bregman, Alex",Bregman,539400,Astros
"Gustave, Jandel",Gustave,537200,Astros
"Paulino, David",Paulino,536100,Astros
"Correa, Carlos",Correa,535000,Astros
"Singleton, Jonathan",Singleton,2000000,Astros
"Kershaw, Clayton",Kershaw,35571429,Dodgers
"Gonzalez, Adrian",Gonzalez,22357143,Dodgers
"Kazmir, Scott",Kazmir,17666667,Dodgers
"Ethier, Andre",Ethier,17500000,Dodgers
"Turner, Justin",Turner,13000000,Dodgers
"Hill, Rich",Hill,12666667,Dodgers
"McCarthy, Brandon",McCarthy,11500000,Dodgers
"Jansen, Kenley",Jansen,11333333,Dodgers
"Puig, Yasiel",Puig,8214286,Dodgers
"Ryu, Hyun-Jin",Ryu,7833333,Dodgers
"Forsythe, Logan",Forsythe,7000000,Dodgers
"Grandal, Yasmani",Grandal,5500000,Dodgers
"Maeda, Kenta",Maeda,3125000,Dodgers
"Romo, Sergio",Romo,3000000,Dodgers
"Wood, Alex",Wood,2800000,Dodgers
"Gutierrez, Franklin",Gutierrez,2600000,Dodgers
"Utley, Chase",Utley,2000000,Dodgers
"Avilan, Luis",Avilan,1500000,Dodgers
"Van Slyke, Scott",Van Slyke,1325000,Dodgers
"Hatcher, Chris",Hatcher,1250000,Dodgers
"Seager, Corey",Seager,575000,Dodgers
"Hernandez, Enrique",Hernandez,555000,Dodgers
"Pederson, Joc",Pederson,555000,Dodgers
"Garcia, Yimi",Garcia,555000,Dodgers
"Baez, Pedro",Baez,550000,Dodgers
"Ravin, Josh",Ravin,545000,Dodgers
"Barnes, Austin",Barnes,540000,Dodgers
"Dayton, Grant",Dayton,540000,Dodgers
"Stripling, Ross",Stripling,540000,Dodgers
"Toles, Andrew",Toles,540000,Dodgers
"Stewart, Brock",Stewart,537500,Dodgers
"Toscano, Dian",Toscano,1600000,Dodgers
"Crawford, Carl",Crawford,21857143,Dodgers
"Guerrero, Alexander",Guerrero,7500000,Dodgers
"Olivera, Hector",Olivera,4666666,Dodgers
"Sierra, Yaisel",Sierra,3500000,Dodgers
"Arruebarruena, Erisbel",Arruebarruena,5500000,Dodgers
"Kemp, Matt",Kemp,2750000,Dodgers