Gebbi
Gebbi

Reputation: 33

D3.js: placing nodes inside other node

Hi stackoverflow community,

i'm looking for a solution to place nodes inside bigger ones with d3 because i need to show that some parts of the graph happen in a specific area.

The input is a JSON object and looks like this:

{
    "nodes": [
        {"id": "sourcSink1", "label": "sourcSink1", "compartment": null},
        {"id": "process1", "label": "process1", "compartment": null},
        {"id": "element1", "label": "element1", "compartment": "compartment1"},
        {"id": "compartment1", "label":"compartment1", "compartment": null},
    ]

    "edges": [
        {"source": "sourcSink1", "target": "process1", "class": 1},
        {"source": "process1", "target": "element1", "class": 2},
    ]
}

The node "compartment1" should contain "element1". Is there a possibility to accomplish this with d3.js?

I'm thankfull for every hint :)

Upvotes: 2

Views: 352

Answers (1)

thatOneGuy
thatOneGuy

Reputation: 10642

Here is a quick example of what could be done : https://jsfiddle.net/thatOneGuy/qhL8wztm/1/

First off adjust the radius. I have put a check in to see if the node has children :

node.each(function(d) {
  if (d.compartment) {
    node.filter(function(e) {
      if (e.name == d.compartment) {
        e.hasChildren = true;
      }
    })
  }
})

Then adjusted the radius. If it has children make it 20 else 5 :

node.attr("r", function(d) {
  if (d.hasChildren) {
    return 20;
  } else {
    return 5;
  }
})

Now to adjust the coordinates. So on tick if the node has a compartment it moves to the node with a name that matches that compartment and sets d.x and d.y accordingly:

 node.attr("cx", function(d) {
      if (d.compartment) {
        node.filter(function(e) {
          return e.name == d.compartment;
        }).each(function(e) {
          d.x = e.x
        })
      }
      return d.x;


    })
    .attr("cy", function(d) {
      if (d.compartment) {
        node.filter(function(e) {
          return e.name == d.compartment;
        }).each(function(e) {
          d.y = e.y
        })
      }
      return d.y;
    });

This isn't perfect, but it works. Obviously it need a bit of playing with, for example, bringing the children in front of the container node etc. But you can get the basics from this :)

EDIT

I have also added a class. If the node has to be in a container, set pointerEvents to none. This is so it doesn't effect the dragging :

.attr("class", function(d){
  if(d.compartment){
  return "node noPointers"
  } else {
  return "node"
  }

  })

CSS :

.noPointers{
  pointer-events:none;
}

Updated fiddle : https://jsfiddle.net/thatOneGuy/qhL8wztm/3/

Upvotes: 3

Related Questions