RedGiant
RedGiant

Reputation: 4748

How to insert after a sibling element in d3.js

I want to insert a foreign object when people zoom in on a circle. I know there is a Insert before :

function zoom(d, i) {
     d3.select("g").insert("foreignObject",'#'+d.name) // insert after a circle in the group
          .attr("width",450)
          .attr("height",450) 
          .attr("id",'f_'+d.name)
          .append("xhtml:div")
          .html(content)

}

but the html content would be obscured by the clicked circle. Is there a insert after a specific sibling so that the foreignObject would be inserted after it?

Don't bother Robert Longson's answer. He just took something he read in the document without proving that works.

@ Code:

var w = 1280,
    h = 800,
    r = 720,
    x = d3.scale.linear().range([0, r]),
    y = d3.scale.linear().range([0, r]),
    node,
    root;

var pack = d3.layout.pack()
    .size([r, r])
    .value(function(d) { return d.size; })

var vis = d3.select("body").insert("svg:svg", "h2")
    .attr("width", w)
    .attr("height", h)
  .append("svg:g")
    .attr("transform", "translate(" + (w - r) / 2 + "," + (h - r) / 2 + ")");

node = root = data;
  console.log(node)
  var nodes = pack.nodes(root);

  vis.selectAll("circle")
      .data(nodes)
    .enter().append("svg:circle")
      .attr("class", function(d) { return d.children ? "parent" : "child"; })
  .attr("id",function(d){ return d.name; })
      .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; })
      .attr("r", function(d) { return d.r; })
      .on("click", function(d) { return zoom(node == d ? root : d); });

  vis.selectAll("text")
      .data(nodes)
    .enter().append("svg:text")
      .attr("class", function(d) { return d.children ? "parent" : "child"; })
      .attr("x", function(d) { return d.x; })
      .attr("y", function(d) { return d.y; })
      .attr("dy", ".35em")
      .attr("text-anchor", "middle")
      .style("opacity", function(d) { return d.r > 20 ? 1 : 0; })
      .text(function(d) { return d.name; });

  d3.select(window).on("click", function() { zoom(root); });


function zoom(d, i) {
  console.log(d)
  var k = r / d.r / 2;
  x.domain([d.x - d.r, d.x + d.r]);
  y.domain([d.y - d.r, d.y + d.r]);

  var t = vis.transition()
      .duration(d3.event.altKey ? 7500 : 750);

  t.selectAll("circle")
      .attr("cx", function(d) { return x(d.x); })
      .attr("cy", function(d) { return y(d.y); })
      .attr("r", function(d) { return k * d.r; });

  t.selectAll("text")
      .attr("x", function(d) { return x(d.x); })
      .attr("y", function(d) { return y(d.y); })
      .style("opacity", function(d) { return k * d.r > 20 ? 1 : 0; });
  if(!d.children)
  {    
    if(!$('#f_'+d.name).length){ 
  d3.select("g").insert("foreignObject",'#'+d.name)
      .attr("width",450)
      .attr("height",450) 
      .attr("id",'f_'+d.name)
      .append("xhtml:div")
      .html('<img src="https://www.gravatar.com/avatar/60163c0083f4d2a54de2bb079a7211f8?s=128&d=identicon&r=PG&f=1">')
    }
  }
  else
  {
     $("foreignObject").attr("opacity",0); 

  }    
  node = d;
  d3.event.stopPropagation();

Upvotes: 12

Views: 18649

Answers (2)

Kreozot
Kreozot

Reputation: 1576

You can use css "element+element" selector (direct adjacent combinator). Let's say you need to insert element "second" right after element "first":

d3.select('body')
    .insert('div', '#first + *')
    .attr('id', 'second');

Upvotes: 21

Robert Longson
Robert Longson

Reputation: 124229

You can use insert to insert the content before any other sibling element, the existing sibling element is the second argument to the insert method.

If you want to insert after element x, you'll need to create a selector that returns the element immediately after x.

If you want to insert after all existing siblings then use append.

Between those two methods you can insert anywhere.

Upvotes: 3

Related Questions