Skip to main content
Bounty Ended with 100 reputation awarded by Mathematics
forgot to mention that each `d` datum associated with a `div.box` contains an array of source objects
Source Link
Milos
  • 619
  • 5
  • 18

Importantly, note that I am here merely using the length of the datum d as the innerText of each box. Keep in mind that that d is a list of objects that share the same x and y. This means that the items of d contain all the information you need to enrich the interface of each div.box (with names, links, images, etc.), or, alternatively, to generate (say, on mouse over event) a second visualisation that would show the list of objects as a "detail" view into the data.

Complete code at: http://jsfiddle.net/vpgsLw1v/2/

Importantly, note that I am here merely using the length of the datum d as the innerText of each box. Keep in mind that that d is a list of objects that share the same x and y. This means that the items of d contain all the information you need to enrich the interface of each div.box (with names, links, images, etc.), or, alternatively, to generate (say, on mouse over event) a second visualisation that would show the list of objects as a "detail" view into the data.

Complete code at: http://jsfiddle.net/vpgsLw1v/2/

Source Link
Milos
  • 619
  • 5
  • 18

Indeed, D3 is perfectly suited for this task (besides being joy to work with). The following is a very simple implementation, which delegates to CSS as much as possible. Much of any further customisation one may need can be implemented simply by fiddling with CSS. The whole thing should be easy to wrap up as a module.

We'll append the result to the #grid-container div:

<div id="grid-container"></div>

We'll work with this fake data:

function rint(limit) { return Math.floor(Math.random() * limit); }

var rowCount = rint(3) + 3;
var colCount = rint(3) + 3;

var data = d3.range(25).map(function(){ // fake data (refresh few times for variety)
    return {
        x: rint(colCount),
        y: rint(rowCount)
    };
});

We'll map data into a three-dimensional array heat according to those x and y values – heat is what we'll visualise with the help of d3. Each heat[y][x] will contain an array of objects with corresponding x and y values:

var heat = d3.range(rowCount).map(function(){ 
    return d3.range(colCount).map(function(){ 
        return []; 
    });
});

data.forEach(function(d){
    heat[heat.length - d.y - 1][d.x].push(d);
});

Find the "hottest" value for the heat map – we'll use this later to generate a full range of colours:

var maxHeat = d3.max([].concat.apply([], heat), function(box){ 
    return box.length; 
});

Now we do the d3 thing. First, generate row selection and populate it with heat data:

var row = d3.select("#grid-container").selectAll(".row").data(heat);

Append the missing div.rows:

row.enter().append("div").attr("class", "row");

Use row selection to create a nested box selection. Each box will inherit the corresponding datum from row's data:

var box = row.selectAll(".box").data(function(d) { return d; });

We now enter and append all the boxes in the grid:

box.enter().append("div")
    .attr("class", function(d, x, y){ 
        var klas = ["box"]
        if (y == heat.length - 1) klas.push("bottom");
        if (x == 0) klas.push("left");
        return klas.join(" "); 
    })
    .attr({
        "data-row": function(d, x, y){ return heat.length - 1 - y; },
        "data-col": function(d, x, y){ return x; }
    })
    .style("background-color", function(d) {
        return d3.hsl( (1 - (d.length / maxHeat)) * 210, 0.8, 0.4 );
    })
    .text(function(d){ 
        return d.length > 0 ? d.length : ""; 
    });

Notice that we are tagging the left column and the bottom row of boxes with classes .left and .bottom, respectively. We are also appending data-row and data-col attributes that we can use to provide CSS content for pseudo elements which will serve as column and row numbers:

    .box.left:before {
        content: attr(data-row);
    }

    .box.bottom:after {
        content: attr(data-col);
    }

Complete code at: http://jsfiddle.net/vpgsLw1v/2/