Search code examples
d3.jsforce-layout

Using data(...) in D3 force layout to update the graph


I am using the D3 force layout and adding code to allow the user to select individual nodes (here, select is not related to D3 select() but indicates the highlighted state of the node from the user's perspective). Only one node should be selected at one time. So, when the user clicks on a node which is not selected, I want to deselect any selected nodes.

I am using an attribute, selected, on the node and struggling to set that using the D3 data(...) or datum(...) method. In fact, I couldn't get it to work like that. What I have now seems kind of kludgy so I am hoping there is a cleaner way.

function deselectAll() {
    var sc = d3.selectAll("circle")
        .filter(function(d) {return (d.selected == "y")});

    var circles = sc.data();

    sc.transition()
        .duration(50)
        .style("stroke-width", "1px")
        .style("stroke", "#3182bd");

    if(null != circles && circles.length > 0) {
        for(i=0; i<circles.length; i++) {
            circles[i].selected = "n";
        }
    }
}

The problem is that adding .data("n") to the chain doesn't set 'selected' to "n". Is it possible to do that as part of the d3 chain?


Solution

  • Many selection (or transition) functions in d3 pass the current datum from each node to your defined functions. You can modify the contents of this datum at any time.

    Using your example, if you want to set the nodes that have d.selected == "y" from "y" to "n" you can use your filtered selection like this:

    sc.each(function(d) {
        d.selected = "n";
    });
    

    This function will be invoked once for each node in the selection (each having "y" for selected as per your filter) so you can simply change the property value on the datum.