D3.js: how to add elements when data is already bound?

I have a problem with D3 joining . I created some circles on the bubble chart. When the user clicks the "show as map" button, I want to convert the circles to a map and then add a label to each one.

Currently clicks handler moves the circles in order and then gives me a JavaScript error, and does not add tags: Uncaught TypeError: Object [object SVGCircleElement]... has no method 'append'

. I know this is because my join syntax is wrong, but how can I fix it?

All the examples I can find for adding new D3 elements are for cases where you are binding new data, not when you already have existing elements with data already bound.

This is my code for creating circles:

  var circle = g.selectAll('.city')
     .data(data).enter()
     .append('circle')
    .attr('class', 'city')
    .attr('r', rfunc).attr("cy", 0)
    .attr("cx", function(d, i) {
      return rdist(d['dist']);
    }).attr("transform", function(d, i) {
      return "rotate(" + d.radial_pos + " 0 0)";
    });

      

And this is my click handler:

d3.select("#layout_geo").on("click", function() {
    // Move the circles - this works OK. 
    var circles = d3.selectAll(".city")
    .transition()
    .duration(1300)
    .attr('cx', function(d) {
       return merc([d['lon'], d['lat']])[0] - 350;
    }).attr('cy', function(d) {
      return merc([d['lon'], d['lat']])[1] - 280;
    });
    // How to add text to each circle?
    circles.append('text')
      .attr('class', 'background')
      .text(function(d) {
         console.log(d.name + ', ' + d.city);
          return d.name + ', ' + d.city;
      })
      .attr('cx', function() {
        return ???;
      }).attr('cy', function() {
        return ???;
      });
  });

      

+3


source to share


1 answer


The problem is that transitioncircles

is not a normal choice . They have a handy feature to remove items, but not add additional items.remove()

append

Another problem is that adding <text>

elements inside the attribute is <circle>

not SVG compliant. In this case, you need to put <text>

and <circle>

inside an element g

like this . The corresponding code change would be:



d3.select("#layout_geo").on("click", function() {
    // Assign the seleciton to circles
    var circleG = d3.selectAll(".city");

    circleG.transition()
      .duration(1300)
      .attr('transform', 'translate(-100, -50)'); // Ignoring the rotation.

    // And append text to the selection
    circleG.append('text')
      .attr('class', 'background')
      .attr('dx', '1em')
      .text(function(d) {
          console.log(d.name + ', ' + d.city);
          return d.name + ', ' + d.city;
      });
  });

      

Note that when the attribute changes, the transform

rotation is lost in the element <circle>

. It can be saved using two nested g elements with a rotation on the outer.

+3


source







All Articles