help me understand selection.node()

Hi!

I’ve happily used D3 on many of projects (via Observable) for a few years now. Thus far, I’ve made it work by cutting a pasting lots of boilerplate from examples. But I’d like to start to get a better handle on how D3 actually works. More specifically, I’m mystified by selection.node(). I’ve learned by trial and error that return svg.node() is necessary if I want to display whatever thing I’ve built with D3. But I don’t actually know the difference between the object constructed in my code (i.e. the object on which I’m calling .node()) and the object returned by .node(). Nor do I understand how calling .node() affects the browser. It also bothers me that I don’t know how to grab the object returned by .node() and ‘reverse’ the process to return the original object on which .node() was called.

So today, I tried to understand by reading the docs. Uh… This is beautifully concise. But goes right over my head. Can anyone point me to a more didactic explanation of the underlying structure of the object on which .node() is called, the object .node() returns, etc. etc.? Perhaps I just need to read an O’Reilly book on D3?

Thank you!!!

2 Likes

Think of D3 as a wrapper around DOM nodes, similar to jQuery. Wrapping the DOM allows both libraries to provide helpers that aren’t available on native DOM nodes and elements.

However, when you want to pass your D3 selection (or jQuery collection) to a native method like the good old Node.insertBefore or the more recent Element.append you run into a problem: The browser has no idea what to do with those objects because they are neither Elements nor Nodes.

That’s why both libraries give you access to the DOM nodes that they manage internally, through methods that return them: selection.node() for D3, and collection.get(0) for jQuery.

2 Likes

That’s hugely clarifying. Thank you!

So one remaining question: does D3 have a function that will take the DOM-readable object returned by node() and transform it back into something parseable by D3?

Yes - that’s exactly the purpose of d3.select and the common term “something parseable by D3” is a “selection”

1 Like

So…just to make sure I have this right…

d3.select(svg.node())

returns whatever selection is pointed to by svg?

That’s correct, yes. You could check that, of course, by creating a notebook with the following in separate cells

// Create a D3 selection:
svg = d3.create("svg").attr("width", 300).attr("height", 150)

// select the selection's node in another cell and modify somehow
d3
  .select(svg.node())
  .append("circle")
  .attr("cx", d3.randomUniform(20, 280)())
  .attr("cy", d3.randomUniform(20, 130)())
  .attr("r", 20)
  .attr("fill-opacity", 0.2)
  .attr("stroke", "black")

// Examine the contents of the node:
svg.node()
1 Like

To complement Mark’s answer, you can also pass a d3 selection or DOM node to selection.append(), but in that case the argument must be a function with the node as return value:

const selection = d3.create("div").style("background", "orange");
const childNode = d3.create("div").text("child").node();
selection.append(() => childNode);
1 Like

@mootari and @mcmcclur thank you for taking the time to explain all this! There are little problems I’ve been unable to solve on several projects that this clears up all at once!

1 Like