Pattern to append an element only once

666 views
Skip to first unread message

Max Goldstein

unread,
Mar 2, 2015, 8:39:02 AM3/2/15
to d3...@googlegroups.com
I'm writing a reusable component, a function that takes a selection g (typically an SVG g element) and draws a chart. The function (done in the standard closure-with-accessors format) can be called multiple times with the same g element but different data. I've found a few places in my code where I want something to happen only the first time the function is called. Here is the best solution I can come up with:

var axis = g.selectAll(".y.axis").data([0]);
axis.enter().append("g").attr("class", "y axis");
axis.call(yAxis);

I don't want more than one y-axis, and so I join to one element. The append operation is only performed the first time. I always want to call yAxis because the scale may have changed. This works, but it seems ugly and is cluttering up my code. Is there a better way? Failing that, is the intent clear from the code?

Any thoughts or feedback appreciated, even if it's just "looks fine to me".

Chris Viau

unread,
Mar 3, 2015, 8:22:06 AM3/3/15
to d3...@googlegroups.com
That's the pattern I'm using too. Another way is to simply use an "if" looking for the presence of your element in the DOM or the equivalent of underscore _.once calling an init function. When I want to add a whole fixture to the DOM once, I like to use a template, since I don't need data binding or DOM generation for these nodes.
ChrisV

nick

unread,
Mar 3, 2015, 1:15:17 PM3/3/15
to d3...@googlegroups.com
Yeah, I think you're doing about the best you can for this case.

Sometimes I use this pattern:
var axis = g.selectAll(".y.axis").data([
0])
 
.call(function(axis){
    axis.enter().append("g").attr("class", "y axis");
  })
  .call(yAxis);

If the insert starts to get unwieldy, you can easily refactor it.

You could also make a wrapper:
var appendOnce = function(parent, selector){
  var bits = selectors.split(".");
  var child = parent.selectAll(selector)
.data([0]);
  child.enter().append(bits[0])
    .attr("class", bits.slice(1).join(" "))
  return child;
}

var
axis =
appendOnce(g, "g.y.axis")
  .call(yAxis);

It's not very d3-like!

Reply all
Reply to author
Forward
0 new messages