Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3.js create objects on top of each other

I create rectangles in my SVG element using this code:

    var rectangles = svg.selectAll("rect")
        .data(dataset)
        .enter()
        .append("rect");

    rectangles.attr("x", function (d) {
            return xScale(getDate(d));
            //return xScale(d.start);
        })
        .attr("y", function (d, i) {
            return (i * 33);
        })
        .attr("height", 30)
        .transition()
        .duration(1000)
        .attr("width", function (d) {
            return d.length;
        })
        .attr("rx", 5)
        .attr("ry", 5)
        .attr("class", "rectangle")
        .attr("onclick", function (d) {
            return "runaction(" + d.start + ")";
        });

How can I create new rectangles on top of the previous ones?

like image 831
Bartosz Avatar asked Aug 09 '13 16:08

Bartosz


1 Answers

This is an answer to this question I got from Scott Murray, author of great introductory tutorials for d3.js http://alignedleft.com/tutorials/d3/ which helped me a lot with understanding its functionality. I hope he won't mind me putting his answer here for everyone's benefit.

Thank you very much Scott!

And yes, that's absolutely possible. Taking your example, let's say you want to draw one set of circles with the dataset called "giraffeData" bound to them. You would use:

svg.selectAll("circle")
    .data(giraffeData)
    .enter()
    .append("circle");

But then you have a second data set (really just an array of values) called "zebraData". So you could use the same code, but change which data set you reference here:

svg.selectAll("circle")
    .data(zebraData)
    .enter()
    .append("circle");

Of course, this will inadvertently select all the circles you already created and bind the new data to them — which isn't really what you want. So you'll have to help D3 differentiate between the giraffe circles and the zebra circles. You could do that by assigning them classes:

svg.selectAll("circle.giraffe")
    .data(giraffeData)
    .enter()
    .append("circle")
    .attr("class", "giraffe");

svg.selectAll("circle.zebra")
    .data(zebraData)
    .enter()
    .append("circle")
    .attr("class", "zebra");

Or, you could group the circles of each type into a separate SVG 'g' element:

var giraffes = svg.append("g")
                .attr("class", "giraffe");

giraffes.selectAll("circle")
    .data(giraffeData)
    .enter()
    .append("circle");

var zebras = svg.append("g")
                .attr("class", "zebra");

zebras.selectAll("circle")
    .data(zebraData)
    .enter()
    .append("circle");

I'd probably choose the latter, as then your DOM is more cleanly organized, and you don't have to add a class to every circle. You could just know that any circle inside the g with class zebra is a "zebra circle".

like image 80
Bartosz Avatar answered Oct 19 '22 18:10

Bartosz