问题
I'm currently working on a force simulation. Here's the fiddle if you need it. This particular section is giving me a bit of trouble - I'm trying it for links and not nodes, where the situation is similar, so we can see the difference of working and non-working:
function linkRender(selection) {
selection
.data(links)
.enter().append("line")
.attr("stroke-width",2)
.attr("stroke","black")
}
let link = svg.selectAll("line").call(linkRender);
Here, call() is supposed to return the selection, that is, the link lines. However, when rendering links in selection.on(), if I use the link variable to work with, it doesn't render properly, much to my distress:
simulation.on("tick",byFrame);
function byFrame() {
// This doesn't work!
link
.attr("x1", d => d.source.x )
.attr("y1", d => d.source.y )
.attr("x2", d => d.target.x )
.attr("y2", d => d.target.y )
// But this works!
svg.selectAll("circle")
.attr("cx",d => d.x )
.attr("cy",d => d.y )
}
And if I assign the link variable to linkRender() the old way, that is,
let link = linkRender(svg.selectAll("line"))
as long as I set linkRender() to return the code inside (return selection.data(links).etc), the rendering works fine.
Finally, here's a comparison of link and svg.selectAll("line"):
So I'd love to know what is going on. Thanks in advance!
回答1:
Your statements are not equivalent and are, therefore, not comparable. The documentation on selection.call() tells us:
Invokes the specified function exactly once, passing in this selection along with any optional arguments. Returns this selection.
This is to be understood literally: returning this selection means exactly the selection the specified function is called with. Because there are no lines prior to that call your selection will be empty when invoking .call() on it. Although you may successfully use this empty selection within that function to bind data and append the lines, this will not alter the original selection.
Because
Selections are immutable.
the .call() method will nonetheless return the original, i.e. empty, selection, which will then be assigned to link. It is easy to see, that this differs from your other solution when selecting the newly added lines afterwards or when returning the enter selection from your modified function.
It is actually working for the nodes, because in byFrame() you do not access them via node but by doing a svg.selectAll("circle") instead.
I think for this application using
let link = linkRender(svg.selectAll("line"));
will be the way to go. As you have already mentioned, this will require linkRender() to return the selection properly.
来源:https://stackoverflow.com/questions/41721841/d3-js-what-is-selection-call-returning