Pytth Pytth - 2 months ago 23
Javascript Question

Method chaining seems to break this d3 code. Why?

This is making me feel exceptionally stupid, but I was messing around with D3js(v4) today, and I am seeing some unexpected behavior regarding the behavior of method chaining.

For this example everything works as expected -- great!

Below is an example of the code that works:

function render (data) {

const circles = svg.selectAll('circle').data(data);

circles.enter().append('circle').attr("r", circleRadius)
.attr("class", "vote")
.attr("cx", function (d) { return d.x})
.attr("cy", function (d) { return d.y});

circles.exit().remove();
}





However, in this example, I ended the
circle
chain to start a new one, and everything breaks.

Below is an example of the problematic code:

function render (data) {

const circles = svg.selectAll('circle').data(data);

circles.enter().append('circle').attr("r", circleRadius);

circles
.attr("class", "vote")
.attr("cx", function (d) { return d.x})
.attr("cy", function (d) { return d.y});

circles.exit().remove();
}


Im sure this is something simple that I am missing, but I really feel like my not understanding this causes me to question some of my fundamental knowledge of JavaScript.

What's more, I'm even more bewildered because I was following along to an example tutorial on youtube. Here is a screenshot of this guys working code that does the same thing I was doing by breaking the chain, but his code works:enter image description here which can be seen in this youtube video.

What the heck am I missing here?

Answer Source

You shouldn't feel dumb — I think everyone who uses d3 does this at some point (me repeatedly).

What's happening is that enter() is returning the selection made when you add data. Everything you chain after enter() will be called on the appended circle created with the enter() selection. That's why your first example works.

When you break chain however, you are now calling attire() on the original selection, which won't work. It's equivalent to doing this:

const circles = svg.selectAll('circle').data(data)
.attr("cx", function (d) { return ( xScale(d.x) )})
.attr("cy", function (d) { return d.y});

To separate them you need to make a new selection:

d3.selectAll('circle')
.attr("cx", function (d) { return ( xScale(d.x) )})
.attr("cy", function (d) { return d.y});

This is a nice way to separate things that will need to be updated.

p.s. I think the reason the youtube video works is because he's using the old version of D3. In the new version, selections are immutable. See the first section here: https://github.com/d3/d3-selection/releases/tag/v1.0.0