How to generate a object consisting of multiple DOM Elements

2,236 views
Skip to first unread message

Berengar Lehr

unread,
Jun 30, 2011, 7:43:14 PM6/30/11
to d3-js
Currently d3 seems to be based on single DOM Elements that might be
augmented by a data-set-array.

My approach is somehow different and I would like to know if - and if
yes how - I could accomplish that.
I have an object in mind that consists of a) an outer circle, b) an
inner circle and c) and third circle inbetween.
Moving the mouse over b or c should make a behave somehow.
Clicking b or c should make a behave a different way.
Now I would like to generate an object that includes these three
circle objects and add it to the base element alike a simple
append("svg:circle") maybe by append(circObject) or appendCircObject()
I tried to combine all necessary append() and attr() calles in one
function and apply it to my base element, but that doesn't work.

So is there a way to do what I want to do using d3?

Mike Bostock

unread,
Jun 30, 2011, 7:44:52 PM6/30/11
to d3...@googlegroups.com
> I have an object in mind that consists of a) an outer circle, b) an
> inner circle and c) and third circle inbetween.

You can append an svg:g element, and then add multiple circles inside.
For example:

var g = vis.append("svg:g").attr("transform", "translate(20,20)");
g.append("svg:circle").attr("r", 10);
g.append("svg:circle").attr("r", 20);
g.append("svg:circle").attr("r", 30);

Mike

Berengar Lehr

unread,
Jun 30, 2011, 8:02:05 PM6/30/11
to d3...@googlegroups.com
This is not exactly what I had in mind.

I was thinking more like a kind-of-class way to go. My idea was to
first build a generic "circles" object, than derive different objects
from that and finally add that objects to the data

var circles = /* some magic happening */
var circleType1 = /* some magic happening */(circles)
var circleType2 = /* some magic happening */(circles)

vis.selectAll(".empty").data(Data1).enter().append(circleType1); (or
.circleType1();)
vis.selectAll(".empty").data(Data2).enter().append(circleType2); (or
.circleType2();)
vis.selectAll(".empty").data(Data3).enter().append(circles); (or .circles();)

I hope now my idea is somewhat clearer.


2011/7/1 Mike Bostock <mbos...@cs.stanford.edu>:

--
Mate ist gesunder Schlaf in Halbliterflaschen

dizzy

unread,
Jul 1, 2011, 1:23:15 AM7/1/11
to d3-js
If the append method must take a string parameter, could you not
create a method on the 'base' circle that will return "svg:circle" and
simply call that method when appending? a crude example might be to
use jquery and do something like this:

var circle = {
prop1: "a",
domElementName: "svg:circle"
};

var circleType1 = $.extend({}, circle, {
prop1: "b", //override method1
prop2: "c" //new method
});

var circleType1 = $.extend({}, circle, {
prop1: "d",
prop2: "e",
prop3: "e",
});

All three types will return "svg:circle" if the domElementName
property is invoked.

vis.selectAll(".empty").data(Data1).enter().append(circleType1.domElementName);
vis.selectAll(".empty").data(Data1).enter().append(circleType2.domElementName);
vis.selectAll(".empty").data(Data1).enter().append(circle.domElementName);



On Jun 30, 8:02 pm, Berengar Lehr <berengarl...@googlemail.com> wrote:
> This is not exactly what I had in mind.
>
> I was thinking more like a kind-of-class way to go. My idea was to
> first build a generic "circles" object, than derive different objects
> from that and finally add that objects to the data
>
> var circles = /* some magic happening */
> var circleType1 = /* some magic happening */(circles)
> var circleType2 = /* some magic happening */(circles)
>
> vis.selectAll(".empty").data(Data1).enter().append(circleType1); (or
> .circleType1();)
> vis.selectAll(".empty").data(Data2).enter().append(circleType2); (or
> .circleType2();)
> vis.selectAll(".empty").data(Data3).enter().append(circles); (or .circles();)
>
> I hope now my idea is somewhat clearer.
>
> 2011/7/1 Mike Bostock <mbost...@cs.stanford.edu>:

Mike Bostock

unread,
Jul 1, 2011, 11:21:04 AM7/1/11
to d3...@googlegroups.com
I don't think I yet understand what you are trying to accomplish. One
comment on your code:

vis.selectAll(".empty")
.data(data)
.enter().append("svg:circle")

While it works to select an empty selection as ".empty", it's more
idiomatic if you declare what you are about to add. This way, you know
you have a way to select those elements again if you need to. For
example, you might say:

vis.selectAll(".type1")
.data(data)
.enter().append("svg:circle")
.attr("class", "type1")

What you're describing sounds a lot like D3's chart templates, so you
might take a look at how they are implemented. A very simple way of
reusing shape templates is to put your code inside a function, and
then call that function whenever you want to instantiate the template.
For example, if I wanted a template that added three circles to a
given container:

function addCircles(g) {
g.selectAll("circle")
.data([10, 20, 30])
.enter().append("svg:circle")
.attr("r", function(d) { return d; });
}

Note that this particular function will do nothing if the specified
`g` selection already contains three circles, but you can easily
change the implementation to handle update and exit. To use the
function, you can say:

addCircles(vis.select("g"));

Or, you can use the `call` operator, which is equivalent and supports
method chaining:

vis.select("g").call(addCircles);

Mike

Reply all
Reply to author
Forward
0 new messages