I have an interactive d3 bar chart (the bars are draggable) with an associated HTML table to keep me aware of the underlying data being charted. I have a Reset button which should reset the chart data to its original state after the user has played with it. What I can't figure out is why the HTML table is resetting correctly while the chart is not. I assume it's something to do with my misunderstanding of d3's enter() / update() but that's as far as my knowledge is getting me.
The full demo is at http://jsfiddle.net/HHLrF/ but here is the relevant code:
d3.json("data/tax_stacked.json", function(error, data) {
var mydata = clone(data); // Use a copy of the data to store user's preference.
var desc = function (a,b) { return d3.descending(+a.value, +b.value);}
mydata.sort(desc);
x.domain([0, d3.max(mydata, function(d) { return +d.value * 1.1; })]);
y.domain(mydata.map(function(d) { return d.name; }));
var defs = chart.insert("defs",".tax");
drawchart();
d3.select("button").on("click",function () {
mydata = clone(data);
mydata.sort(desc);
drawchart();
});
function drawtable() {
console.log("drawing table...");
var columns = ["name", "value"];
var table = d3.select("#grid");
table.html("");
var thead = table.append("thead"),
tbody = table.append("tbody");
// append the header row
thead.append("tr")
.selectAll("th")
.data(columns)
.enter()
.append("th")
.text(function(column) { return column; });
// create a row for each object in the data
var rows = tbody.selectAll("tr")
.data(mydata)
.enter()
.append("tr");
// create a cell in each row for each column
var cells = rows.selectAll("td")
.data(function(row) {
return columns.map(function(column) {
return {column: column, value: row[column]};
});
})
.enter()
.append("td")
.text(function(d) { return d.value; });
}
function drawchart() {
var drag = d3.behavior.drag()
.on("dragstart", function(){
var where;
d3.select(this).classed({"bar": true, "moved": true});
})
.on("drag", function(d){
where = d3.event.x;
d3.select(this)
.attr("width", where);
d3.select(this.nextSibling) // Move the label
.attr("x", where + 10);
/* Some lines omitted here for brevity */
d.value = parseInt(x.invert(where));
drawtable();
})
.on("dragend", function(d){
});
var taxes = chart.selectAll(".tax")
.data(mydata)
.enter()
.append("g")
.classed("tax", true);
// Clipping paths to swap colours when the user's figures exceed original figures (work in progress!)
defs.selectAll(".cp")
.data(mydata)
.enter()
.append("clipPath")
.attr("id", function (d,i) { return "cp" + i; })
.append("rect")
.attr("x", function(d) { return x(d.value); })
.attr("y", function(d) { return y(d.name); })
.attr("width", function(d) { return width - x(d.value); })
.attr("height", y.rangeBand());
// Light blue bars for the original budget figures
taxes.append("rect")
.classed("original", true)
.attr("x", function(d) { return x(0); })
.attr("y", function(d) { return y(d.name); })
.attr("width", function(d) { return x(d.value); })
.attr("height", y.rangeBand())
.attr("rx",0);
// Medium blue bars for the user's budget figures
taxes.append("rect")
.attr("class", function (d) { return (d.fixed == "y") ? "fixed" : "bar"; })
.attr("x", function(d) { return x(0); })
.attr("y", function(d) { return y(d.name)+1; })
.attr("width", function(d) { return x(d.value); })
.attr("height", y.rangeBand()-1)
.attr("rx",0)
.attr("title", function (d) { return d.value; })
.attr("clipper", function (d,i) { return "url(#cp" + i + ")"; });
// .attr("clip-path", function (d,i) { return "url(#cp" + i + ")"; });
taxes.append("text")
.attr("class", "label")
.attr("x", function(d) { return x(d.value)+10; })
.attr("y", function(d) { return y(d.name); })
.attr("dy", "1.6em")
.text( function (d) { return d.name; });
var iconptr = taxes.append("g")
.classed("smooth", true)
.attr("transform", function (d) {
return "translate(" + (x(d.value)+labelpadding+parseInt(this.previousSibling.getBBox().width)) + "," + (y(d.name)+7) + ") scale(0.037,0.037)";
});
/* Some lines omitted here for brevity */
// Draw the axes
chart.append("g")
.attr("class", "x axis")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
d3.selectAll(".bar").call(drag);
drawtable();
}
});