问题
I am puzzling over why the following simple update pattern doesn't work. This follows the recommended General Update Pattern , as far as I can see.
<script src="https://d3js.org/d3-selection.v1.min.js"></script>
...
var dat = ["One","Two","Buckle my shoe"];
var sel = d3.selectAll("p.test").data(dat);
sel.enter().append("p").classed("test", true);
sel.exit().remove();
//update 1 ... doesn't work
sel.text(function(d) { return d;})
The paragraphs get created fine, but the text isn't set. However, if I do this:
//update 2 ... this works as expected
d3.selectAll("p.test").text(function(d) { return d;});
...everything works fine. The first version has always worked in the past.
Update: I tried using the full d3 library ...
<script src="https://d3js.org/d3.v4.min.js"></script>
... and the first version works again. Do I need more than d3.selection?
To clarify, my past practice has been to define a separate update function that takes the selection as a parameter. Eg, function doUpdate(sel) { sel.text(...);}
This is for cases where I expect the data elements to have few changes in size, but many changes in content. Storing the selection as a variable and repeatedly running updates on it has worked well before.
回答1:
So after studying the release notes, it seems this is not going to be backwardly compatible, for some good reasons. First, the short answer:
Replace this:
sel.enter().append("p").classed("test", true);
...
sel.text(function(d) { return d;}) //update block
with this:
var update = sel.enter().append("p").classed("test", true).merge(sel);
...
update.text(function(d) { return d;}) //update block
The reason for this is described in this article (thanks @mbostock) and is a fix for empty selector problems with v3. The point I missed at first was that the enter() block needs to run first so that the merge() block has a populated selection to work on. Which means that the merge() call must come off the end of the enter() block chain.
The format of the change documents sort of hid that, because many examples use chains of function calls. I'm used to splitting the enter/update blocks into separate variables. This aids readability (usually) and means I can farm out the enter/update actions to separate functions - more reusable code that way.
So with that in mind, this doesn't work:
var enter = sel.enter();
var update = enter.merge(sel); //Nope! Not populated at this point.
enter.append(...); //too late! Update block uses an empty selection.
But this works okay
var enter = sel.enter();
enter.append(...);
var update = enter.merge(sel); //defined after block is populated
来源:https://stackoverflow.com/questions/38905428/general-update-pattern-not-working-as-expected-with-d3-selection