Skip to content

Instantly share code, notes, and snippets.

@LeeMendelowitz
Last active December 6, 2018 23:02
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save LeeMendelowitz/11383724 to your computer and use it in GitHub Desktop.
Save LeeMendelowitz/11383724 to your computer and use it in GitHub Desktop.
D3 Dynamic Table with Nested Data

This script is another demonstration of the update, enter, and exit selections in D3. The script also makes use of key functions to bind data to each row, and transitions to animate opacity and delay the removal of rows. This script demonstrates nested data, where an array of values is bounded to each row (<tr>), and then a single value is bound to each cell (<td>) element in the row.

Each row of the table has a unique letter key, which is displayed as the first cell. The remainder of the row is an array of random digits. At each iteration:

  1. A new set of letter keys is randomly selected from the alphabet.
  2. For each letter in the set, a new row with random data will be inserted in the table if the letter is not already in the table.
  3. Any row in the table with a key which is not in the new set is removed.
  4. The number of columns in the table is randomly adjusted. If the new number of columns is larger than the current table, random data is appended to each row. Otherwise, cells are deleted from each row.

Cells which remain between iterations are colored in blue, new cells are colored in green, and cells which are to be deleted are colored red before removal.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Dynamic Table</title>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<style>
body {
font: 24px monospace
}
table {
margin: 10px;
font-size: 24px;
}
td {
padding: 5px;
border-bottom: 1px solid black;
}
td.update {
color: blue;
}
td.enter {
color: green;
}
td.exit, tr.exit td {
color: red;
}
td.row-header {
border-right: 1px solid black;
font-weight: bold;
}
</style>
</head>
<body>
<table>
</table>
</body>
<script>
///////////////////////////////////////////
// UTILITY FUNCTIONS
// Make a key-value object
var make_key_value = function(k, v) {
return { key: k,
value: v
};
};
// Join a key array with a data array.
// Return an array of key-value objects.
var merge = function(keys, values) {
var l = keys.length;
var d = [], v, k;
for(var i = 0; i < l; i++) {
v = values[i].slice();
k = keys[i];
d.push( make_key_value( k, v ));
}
return d;
};
// Shuffles the input array.
function shuffle(array) {
var m = array.length, t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m], array[m] = array[i], array[i] = t;
}
return array;
}
// Returns a random integer between min and max
// Using Math.round() will give you a non-uniform distribution!
function get_random_int(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Resize the array, append random numbers if new_size is larger than array.
function update_array(a, new_size) {
a = a || [];
if (a.length > new_size) {
return a.slice(0, new_size);
}
var delta = new_size - a.length;
for(var i = 0; i < delta; i++) {
a.push(get_random_int(0, 9));
}
return a;
};
////////////////////////////////////////////////////////////
// GENERATE DATA
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
var letter_to_data = {}; // store row data
var generate_data = function() {
var i, j, a, l;
var letters = shuffle(alphabet);
var num_cols = get_random_int(3, 10);
var num_rows = get_random_int(5, 15);
var row_data = []
for (i = 0; i < num_rows; i++ ) {
l = letters[i];
a = update_array( letter_to_data[l], num_cols );
letter_to_data[l] = a; // store data
row_data.push( a );
}
for (i = num_rows; i < letters.length; i++) {
delete letter_to_data[i];
}
letters = letters.slice(0, num_rows);
return merge(letters, row_data);
};
/////////////////////////////////////////////
// DEFINE HELPER FUNCTIONS
// Extract key from key-value object.
var get_key = function(d) { return d && d.key; };
// Extract data from a key-value object.
// Prepend the key so it is the first item in the values array.
var extract_row_data = function(d) {
var values = d.value.slice();
// Prepend the key
values.unshift(d.key);
return values;
};
// Use data as is.
var ident = function(d) { return d; };
/////////////////////////////////////////////
// UPDATE THE TABLE
// Select the table element
var table = d3.select('table');
// Define function to update data
var update = function(new_data) {
var rows = table.selectAll('tr').data(new_data, get_key);
//////////////////////////////////////////
// ROW UPDATE SELECTION
// Update cells in existing rows.
var cells = rows.selectAll('td').data(extract_row_data);
cells.attr('class', 'update');
// Cells enter selection
cells.enter().append('td')
.style('opacity', 0.0)
.attr('class', 'enter')
.transition()
.delay(900)
.duration(500)
.style('opacity', 1.0);
cells.text(ident);
// Cells exit selection
cells.exit()
.attr('class', 'exit')
.transition()
.delay(200)
.duration(500)
.style('opacity', 0.0)
.remove();
//////////////////////////////////////////
// ROW ENTER SELECTION
// Add new rows
var cells_in_new_rows = rows.enter().append('tr')
.selectAll('td')
.data(extract_row_data);
cells_in_new_rows.enter().append('td')
.style('opacity', 0.0)
.attr('class', 'enter')
.transition()
.delay(900)
.duration(500)
.style('opacity', 1.0);
cells_in_new_rows.text(ident);
/////////////////////////////////////////
// ROW EXIT SELECTION
// Remove old rows
rows.exit()
.attr('class', 'exit')
.transition()
.delay(200)
.duration(500)
.style('opacity', 0.0)
.remove();
table.selectAll('tr').select('td').classed('row-header', true);
};
// Generate and display some random table data.
update(generate_data());
setInterval(function() {
update(generate_data());
}, 3500);
</script>
</html>
@omundy
Copy link

omundy commented Jan 16, 2017

Google defaults to https links where possible so your block can't load d3 lib over http. I assume you can fix this with https d3 link?

(index):1 Mixed Content: The page at 'https://bl.ocks.org/LeeMendelowitz/11383724' was loaded over HTTPS, but requested an insecure script 'http://d3js.org/d3.v3.min.js'. This request has been blocked; the content must be served over HTTPS.
(index):166 Uncaught ReferenceError: d3 is not defined
    at (index):166

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment