add/delete nodes + selecting the style of a node

3,002 views
Skip to first unread message

neotrex

unread,
Jun 30, 2011, 1:09:34 PM6/30/11
to d3-js
Hello everybody,

I started to do a force directed layout.
I have 2 problems:

1) how do I select the visible or hidden property of a node (i've
tried lots of stuff from d.style.visible to d.parent.style visible and
other combinations, but none seemed to work.... :( )

2) I try do delete/add nodes to a graph according to the level and the
group of the node.
Something like the next code seems to work ok to just delete the
children when clicking on a node, but when I click on the another node
it deletes the children of that node, but redraws the children of the
previously selected node....
Hence without solving problem 1) it seems like I will not do any
progress.

function clicknode(d,i) {
gr = d.group;
dn = d.name;
dl = d.level;

var link = vis.selectAll("line.link")
.style("display", function(d) {if (d.target.level > dl &&
d.target.group == gr){return "none";}});
var node = vis.selectAll("circle.node")
.style("display", function(d) {if (d.level > dl && d.group == gr)
{return "none";}})
var text = vis.selectAll("text.txt")
.style("display", function(d) {if (d.level > dl && d.group == gr)
{return "none";}})
//
}


By doing the reverse stuff:
var link = vis.selectAll("line.link")
.style("display", function(d) {if (d.target.level > dl &&
d.target.group == gr){return "";}});

obviously I will be able to redraw the children of that node...


Best regards,
neotrex

Mike Bostock

unread,
Jul 1, 2011, 11:31:26 AM7/1/11
to d3...@googlegroups.com
> 1) how do I select the visible or hidden property of a node

There are at least four ways to control node visibility:

* the "visibility" style
* the "display" attribute
* the "opacity" style
* the "fill-opacity" and "stroke-opacity" style

There's a good summary in this thread:

http://groups.google.com/group/d3-js/browse_thread/thread/6021c3bfa8a215ae

If you have circles, you could also transition the radius to or from
0, which is aesthetically pleasing.

> var link = vis.selectAll("line.link")
>                .style("display", function(d) {if (d.target.level > dl &&
> d.target.group == gr){return "";}});

This isn't doing what you expect, because the style operator will
remove the property if the return value from your function is null or
undefined. So this is actually making all of the nodes visible, rather
than only affecting the ones you intend. A better way to do this is to
filter the selection, so that you only toggle the style property on
some of the nodes. For example, to clear the display property and make
some nodes visible:

vis.selectAll("line.link")
.filter(function(d) { return d.target.level > dl &&
d.target.group == gr; })
.style("display", null);

Similarly, to hide some nodes:

vis.selectAll("line.link")
.filter(function(d) { return d.target.level > dl &&
d.target.group == gr; })
.style("display", "none");

Mike

Mike Bostock

unread,
Jul 1, 2011, 11:32:43 AM7/1/11
to d3...@googlegroups.com
The relevant comment from the documentation:

"""
A null value will remove the style property.
"""
https://github.com/mbostock/d3/wiki/Selections#style

Also, note that null == undefined in JavaScript!

Mike

neotrex

unread,
Jul 1, 2011, 12:19:10 PM7/1/11
to d3...@googlegroups.com
Hello Mike!

Thanks for illuminating me! I read that post several times and still didn't fully grasp it.

The reverse, for showing nodes also works fine:
vis.selectAll("line.link")
      .filter(function(d) { return d.target.level > dl && d.target.group == gr; })
      .style("display", "");


You are right regarding undefined. I already knew that.

Best regards,
neotrex

neotrex

unread,
Jul 2, 2011, 1:45:27 PM7/2/11
to d3...@googlegroups.com
Hello all,

While everything from previous posts worked, I still can't find 
how to recursively delete all the children of a node (and their children, and so on.)
using D3 functions.
There seems to be no way to select all the children (and all the children's children)
of a certain parent :(.

I've tried each and I've looked at this post (and the related examples), 
but still no result.

Best regards,
neotrex

Mike Bostock

unread,
Jul 2, 2011, 2:57:39 PM7/2/11
to d3...@googlegroups.com
If you remove a parent, all its children are implicitly deleted (they are detached from the DOM). Do you need to recursively delete the children or is just removing the parent sufficient?

If you really want to select all descendants of a given element, you can selectAll("*"), but this is rarely needed.

Mike

neotrex

unread,
Jul 2, 2011, 3:34:03 PM7/2/11
to d3...@googlegroups.com
Hello Mike,

I've already noticed that deleting a parent deletes all his children. I did tested my code...and applied your fix.
I want to click on a node and remove only all his children (regardless of the number of levels) recursively of course, but not the node itself,
pretty much like in the code I've showed.
A code similar to that works on some cases, but not in all cases...
So I tought, I should try a recursive approach, or a method to access parent's data....
And this is pretty much where I got stucked, because doing something recursive in java, c++ or even javascript is ok,
however I don't know how to do it using D3's select or filter :(. 
Couldn't find examples.... Tried with closures, each operator....didn't worked.
Thanks for answering so fast.

Best regards,
neotrex

Mike Bostock

unread,
Jul 2, 2011, 8:34:21 PM7/2/11
to d3...@googlegroups.com
OK, I think I see the problem: the hierarchy in your tree layout isn't
reflected in the document structure. So, if you want to select all the
descendants of a particular node in the tree, you have to do that
through the tree data structure, rather than using selectors on the
document. The list of links and nodes are flattened (which you will
see if you inspect the document using the browser's development
tools).

Here's how you might select descendant nodes by traversing the tree structure:

node.on("click", function(p) {

// remove links that are connected to descendants
link.filter(function(d) {
for (d = d.source; d; d = d.parent) {
if (d === p) return true;
}
}).remove();

// remove all descendants
node.filter(function(d) {
while (d = d.parent) {
if (d === p) return true;
}
}).remove();

});

Of course, this isn't the best implementation for a few reasons:

1. The layout should adjust when the child nodes are removed.
2. You may want to show hidden child nodes again (i.e., undo the hide).

And why not add animated transitions to smoothly expand and collapse
the tree? :)

http://bl.ocks.org/1061834

Mike

Jon Frost

unread,
Jul 2, 2011, 8:38:56 PM7/2/11
to d3...@googlegroups.com
Looks great - excellent work.
Thanks for sharing.


On Sat, Jul 2, 2011 at 7:34 PM, Mike Bostock <mbos...@cs.stanford.edu> wrote:
...

neotrex

unread,
Jul 3, 2011, 5:52:47 AM7/3/11
to d3...@googlegroups.com
Hello all,

As I've said in the first post from this thread I'm doing force directed layout.
While it is true that the data structure is a tree, the example you posted is not recursive and therefore it does not work.
It just deletes the links of the children from the next level, not all the links and nodes of all of one node's children.

I tried to modify your function to work recursively, but I always get either an undefined 'd' or 'p'.
If I put an alert inside the for it always shows me an undefined d or p.... :(
But I think recursion should work. 

Is there really no way to do what you did with layout.tree with a layout.force?

Most of the times in practice you would display a tree using a force directed layout because of several reasons
- you want the first node of the tree in the center (you already offered a solution for this in a previous post)
- you might want to develop only 1-2 nodes on more levels....
case in which it is clear that a tree layout would look ugly, and a force directed would display better

I would want to toggle on a hasChildren function.

BTW your example for layout.tree is awesome.

Best regards,
neotrex

Mike Bostock

unread,
Jul 3, 2011, 10:16:59 AM7/3/11
to d3...@googlegroups.com
> While it is true that the data structure is a tree, the example you posted
> is not recursive and therefore it does not work. It just deletes the links
> of the children from the next level, not all the links and nodes of all of
> one node's children.

Untrue. You don't need to use recursion to traverse a tree. Look
closely at the filter:

function(d) {
while (d = d.parent) {
if (d === p) return true;
}
}

The while loop causes the code to walk up the parent chain, starting
at the current node (d). If, while traversing ancestors, the toggle
node (p) is found, then we know that the current node (d) is a
descendant of the toggle node. If I wanted to only detect direct
children instead, I would say:

function (d) {
return d.parent === p;
}

> I tried to modify your function to work recursively, but I always get either
> an undefined 'd' or 'p'.

This is a JavaScript error. Without posting your code, it's difficult
to say what the problem will be. In my example, there are two nested
functions:

1. The `on` operator registers a "click" event listener. This defines
the node `p` ,which represents the node that was clicked on.

2. Within the event listener, the `filter` operator is applied to the
nodes. This invokes another function on each node, defining a node
`d`, which represents every other node.

There's a concept in JavaScript called closure which is what allows
the function passed to the `filter` operator to access not just it's
direct arguments (d, i) but also the arguments and variables in
enclosing functions (p).

Mike

Mike Bostock

unread,
Jul 3, 2011, 11:01:05 AM7/3/11
to d3...@googlegroups.com
And here's the force-layout toggling example:

http://bl.ocks.org/1062288

Mike

neotrex

unread,
Jul 3, 2011, 1:47:25 PM7/3/11
to d3...@googlegroups.com
Hello Mike,

Yes. I have used closures before. And I did noticed how you traversed the tree.

I will try to post my code for future examples.

Also thanks for this example. I think I will be able to modify it to make most of the effects I want to achieve.

Best regards,
neotrex

Barbara Peng

unread,
Dec 5, 2013, 4:27:03 PM12/5/13
to d3...@googlegroups.com, mbos...@cs.stanford.edu
Hi Mike!  I read through your post and it was extremely helpful.  One (hopefully) quick question. I am trying to use your filter function and filter out nodes (in a force directed graph) that do not have any links attached to them- problem is I'm having trouble defining that parameter.  I tried to use link.source and say that if the node is not a source or target of a link, then to hide it but it has not been working.

Here are 2 excepts from my code:

    links.forEach(function(link) {
         link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
         link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
    });

     svg.selectAll(".node")
     .filter(function(d) { return d.index !== link.source  || d.index !== link.target; }) 
      .style("display", function(d) {
if (C1 == 1) {return "none";}
if (C1 == -1) {return "";}});
     C1=C1*(-1);


Any guidance is greatly appreciated!

Barbara



---
Reply all
Reply to author
Forward
0 new messages