what's the shortest/fastest idiom to only append new nodes when they don't exist yet [without using .data().enter()]?

2,446 views
Skip to first unread message

Ger Hobbelt

unread,
Feb 23, 2012, 1:25:54 PM2/23/12
to d3...@googlegroups.com
If I got it right, then the preferred idiom to produce a series of nodes as children of a parent is:

d3.select(parent).selectAll(child-selector).data(series-array).enter().append(child-node)

now here I am, ashamed and amazed that I can't come up with how to generate children if they *possibly* don't exist yet in some fast, concise and clean-looking d3 code, if these conditions apply:

1) you do not know [yet] if the given child node(s) exist, so the code must find out and adjust as it goes along (that is: under the hood, in d3, there's an if() condition in there somewhere, one way or another)

2) you do not have any data for the parent to drive the code, i.e. .data(series).enter() isn't part of the idiom to be.


#2 is optional; if d3 is designed to /require/ .data() then we can always succumb and fake a range.


In other circumstances I'd do this kind of HTML manipulation in mootools, but to load moo for just this bit of "if (!$('node')) create-node" feels quite dirty and I believe d3 is able to do it all by itself, but somehow I'm completely stuck and don't see the way. Database folks would say I'm looking for the UPSERT (update/insert) syntax in d3 here.

...

If you need a sample scenario to visualize what I'm saying here, here's a very basic exercise: imagine you start with html with an empty <div> like this:

<html>
<body>
<div id="parent"></div>
</body>
</html>

and then there's a bit of setInterval() repeating javascript which always executes the same code which initially adds a <p> child to the <div> with a bit of .text() where a random value is printed and in the next intervals that same code updates that <p> node with a new .text() value. No if()s (or a?b:c) in the user code.
Since this smells a lot like 'animation' it's important to note that it isn't, at least it's to be done in an explicit setInterval() -- otherwise you'ld be able to provide me with an d3.transition based answer which is precisely not what I'm looking for.  ;'-)


I ask this generically as I don't 'see' the mechanism/idiom to use; that's what I'm looking for as this situation indicates to me that I don't grok an important-if-unidentified part of d3. d3 feels like design genius an I'm feeling pretty darn stupid right now.



Met vriendelijke groeten / Best regards,

Ger Hobbelt

--------------------------------------------------
web:    http://www.hobbelt.com/
        http://www.hebbut.net/
mail:   g...@hobbelt.com
mobile: +31-6-11 120 978
--------------------------------------------------

Mike Bostock

unread,
Feb 23, 2012, 1:39:31 PM2/23/12
to d3...@googlegroups.com
For your scenario:

var p = d3.select("body").selectAll("p").data([null]);
p.enter().append("p"); // create <p>, if needed
p.text(Math.random); // display a random value

Here [null] is your data array: one null. You could put real data
instead of null in there, of course.

Mike

Ger Hobbelt

unread,
Feb 23, 2012, 1:55:11 PM2/23/12
to d3...@googlegroups.com
Mike, 

I now understand that d3 _requires_ a data() set (it's mandatory) as one needs the .selectAll().data() chain to conditionally create/update nodes. I didn't recognize that I had to use a 'var p' that way, while I suspected the data() requirement.  :-((

Thanks for your clear reply! Sorry I had to bother you with this.


Mike Bostock

unread,
Feb 23, 2012, 2:32:34 PM2/23/12
to d3...@googlegroups.com
The other way you can do this without data is to use control flow:

var p = d3.select("p");
if (p.empty()) = p = d3.select("body").append("p");
p.text(Math.random);

It's a question as to whether you prefer control flow (jQuery style)
or using D3's data join. The nice thing about the data join is that it
extends to variable numbers of elements and hierarchical structures.
;)

Mike

Ger Hobbelt

unread,
Feb 24, 2012, 2:01:03 AM2/24/12
to d3...@googlegroups.com
Thanks for the .empty() mention (it will help in other situations), but control flow coding isn't what I sought.

As I said I felt I hadn't grokked d3 design and it's the data flow approach that makes the d3 user code look the way it 'should', at least in my eye. And exactly that approach is the learning curve I've got to go through; if I don't by circumventing via coding it 'jQuery style' the ducking away will definitely bite my bottom later on. And then it'll leave more than just bite marks. :-)

Besides, I'm going to use it now in a scenario where I need to 'upsert' a series of nodes: the data/enter approach can do that with pretty lean notation.

Cheers and thanks for the guidance,

Ger
Reply all
Reply to author
Forward
0 new messages