Skip to content

Instantly share code, notes, and snippets.

@alexland
Last active August 29, 2015 14:08
Show Gist options
  • Save alexland/fa812d82560d508bc0c3 to your computer and use it in GitHub Desktop.
Save alexland/fa812d82560d508bc0c3 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>dynamic data in d3: data join & update-append-exit pattern</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1"><!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<style>
#gr2 {
padding-top: 50px;
}
.row {
margin-bottom: 0;
padding-bottom: 0;
}
#totalCells {
color: gray;
font-style: italic;
font-weight: 300;
}
#cellsAdded {
color: #539B95;
text-indent: 8px;
}
#cellsUpdated {
color: #FADA5E;
text-indent: 8px;
}
#cellsRemoved {
color: #0077BE;
text-indent: 8px;
}
line, path {
fill: none;
stroke: gray;
shape-rendering: crispEdges;
}
.xAxis {
font-size: 10px;
font-family: sans-serif;
shape-rendering: crispEdges;
}
.tick {
fill: navy;
stroke: none;
}
.tick > text {
font-style: normal;
font-size: 11px;
/*font-style: bold;*/
font-family: "helvetica";
}
.enter {
/*fill: #3B7A57;*/
}
.update {
/*fill: #062A78;*/
}
.exit {
/*fill: #D3212D;*/
}
#nctu #ntlu {
margin-left: 7px;
margin-top: 5px;
text-align: center;
/* padding-top: 2px;*/
width: 25px;
height: 15px;
font-size: 12px;
font-color: #0047AB;
font: "Gill Sans Light" ;
}
#widgets {
margin-top: 0;
}
.span12 {
margin-top: 20px;
margin-bottom: 20px;
}
table {
border-collapse: collapse;
/* padding: 10px;*/
}
td {
padding-left: 5px;
padding-right: 5px;
margin-top: 0;
margin-bottom: 0;
padding-top: 0;
padding-bottom: 0;
}
tr {
margin-top: 0;
margin-bottom: 0;
padding-top: 0;
padding-bottom: 0;
}
#val {
text-align: center;
background: #6A8799;
color: #FFFFFF;
}
#name {
text-align: left;
color: navy;
}
</style>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script>
</script>
</script>
</head>
<body>
<div class="container">
<div class="row">
<div class="span12" id="sp-all"></div>
</div>
<div class="row">
<div class="span1" id="sp-l"></div>
<div class="span3" id="gr1">
<table class="table table-bordered table-hover table-condensed">
<tr>
<td id="name">
<p id="totalCells">
total cells
</p>
</td>
<td id="val">
<p id="tnc"></p>
</td>
</tr>
<tr>
<td id="name">
<p id="cellsAdded">
cells added
</p>
</td>
<td id="val">
<p id="nca"></p>
</td>
</tr>
<tr>
<td id="name">
<p id="cellsRemoved">
cells removed
</p>
</td>
<td id="val">
<p id="ncr"></p>
</td>
</tr>
<tr>
<td id="name">
<p id="cellsUpdated">
cells updated
</p>
</td>
<td id="val">
<p id="ncu"></p>
</td>
</tr>
</table>
</div>
<div class="span6" id="gr2"></div>
<div class="span1" id="sp-r"></div>
</div>
<div class="row" id="widgets">
<div class="span5" id="sp-l"></div>
<div class="span2" id="widgets">
<p>
<button type="button" id="btn0" class="btn btn-primary">
data update
</button>
</p>
</div>
<div class="span5" id="sp-r"></div>
</div>
</div>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</div><script src="http://d3js.org/d3.v3.min.js">
</script>
<script type="text/javascript">
$(function() {
function genData() {
np = 3 + Math.floor(15*Math.random());
return d3.range(np);
};
var SVG_HEIGHT = 240,
SVG_WIDTH = 500,
MARGIN_L = 50,
CELL_WIDTH = 25,
CELL_HEIGHT = 7,
CELL_SPACER = 1.5,
CELL_WIDTH_EFF = CELL_WIDTH + CELL_SPACER,
CELL_HEIGHT_EFF = CELL_HEIGHT + CELL_SPACER;
//----------------- main svg viewport ---------------//
var svg = d3.select("#gr2")
.append("svg")
.attr("width", SVG_WIDTH)
.attr("height", SVG_HEIGHT)
.append("g")
.attr("transform", "translate(50," + (SVG_HEIGHT / 3) + ")");
//------------------ axis ---------------------//
var xScale = d3.scale.linear()
.range([0, SVG_WIDTH - MARGIN_L])
.domain([0, 10]);
svg.append("g")
.attr({
"class": "xAxis",
})
.attr("transform", "translate(0,10)")
var xAxis = d3.svg.axis()
.orient("bottom")
.tickFormat(d3.format("d"))
.scale(xScale);
//----------------- user interaction -----------------//
$("#btn0").on("click", update);
//---------------- event handler ---------------//
function update() {
dataset = genData()
updatePlot(dataset);
}
//----------to calculate values for display in table ---------//
function cellCounter(dataLen) {
var nc_last = $("#tnc").text();
nc_current = dataLen,
diffnc = Number(nc_last) - nc_current;
if (diffnc < 0) {
var ncr = 0,
nca = -diffnc,
ncu = nc_last;
}
else if (diffnc === 0) {
var ncr = 0,
nca = 0,
ncu = nc_last;
}
else if (diffnc > 0) {
var ncr = diffnc,
nca = 0,
ncu = nc_current;
}
return [dataLen, nca, ncr, ncu];
}
//-------------- main fn to update data view -------------//
function updatePlot(dataset) {
res = cellCounter(dataset.length);
$("#tnc").text(res[0]); // total number of cells
$("#nca").text(res[1]); // num cells added
$("#ncr").text(res[2]); // num cells removed
$("#ncu").text(res[3]); // num cells updated
// Step I: DATA JOIN
var cells = svg.selectAll("rect")
// a key function is the 2nd, optional,
// argument passed to .data
.data(dataset, function(d) {return d;});
// Step II: update-append-exit
// UPDATE (update extant nodes)
cells
.attr("class", "update")
.transition()
.duration(250)
.attr({
x: function(d, i, j) {return i * CELL_WIDTH_EFF;},
width: CELL_WIDTH,
height: CELL_HEIGHT,
fill: "#FADA5E" // updated cells
});
// ENTER (create new nodes)
cells
.enter()
.append("rect")
.attr({
"class": "enter",
x: 0,
y: -75,
width: CELL_WIDTH,
height: CELL_HEIGHT,
fill: "#539B95", // new cells
"fill-opacity": 1e-6,
});
cells
.data(dataset, function(d) {return d;})
.transition()
.delay(500)
.ease("cubic-in-out")
.duration(500)
.attr({
x: function(d, i, j) {
return i * CELL_WIDTH_EFF;
},
y: -75,
"fill-opacity": 1
});
// EXIT (remove extant nodes having no data)
cells
.exit() // analogous to .enter()
.attr({
"class": "exit",
fill: "#0077BE" // exiting cells
})
.transition()
.delay(750)
.ease("cubic-in-out")
.duration(250)
.attr({
x: 0,
y: -75,
"fill-opacity": 1e-6
})
.remove(); // analogous to append()
// update the axis
rhi = (dataset.length*CELL_WIDTH) + 25;
xScale.domain([0, dataset.length]);
xScale.range([0, rhi]);
svg.select(".xAxis")
.transition()
.duration(250)
.call(xAxis);
}
updatePlot(genData());
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment