Iterate over already created nodes in D3js

Natasha picture Natasha · Dec 10, 2014 · Viewed 25.4k times · Source

I am using a force-directed graph and am adding newly created nodes to the graph. This works perfectly fine.

My node has a property called "state" that keeps changing. The problem I'm facing is that, in my code, the condition to check the "state" is only checked when a new node comes in. When theres an update, my JSON is updated with the state but the execution doesn't reach the code for checking the state.

Find my code below:

function buildGraph() {
                // Update link data
                link = link.data(links);

                // Create new links
                link.enter().append("g")
                .attr("class", "link")
                .each(function (d) {
                    d3.select(this)
                    .append("line")
                    .attr("stroke", "#000")
                    .attr("stroke-width", 2)
                    .attr("opacity", 0.3);
                });

                // Delete removed links
                link.exit().remove();

                // Update node data
                node = node.data(nodes);

                // Create new nodes
                node.enter().append("g")
                .attr("class", "node")
                .each(function (d) {
                    if (d.state == "0") {                      // doesn't come here after an update
                        var imgId = d.name;
                        d3.select(this).append("svg:image")
                        .attr("class", "spinner")
                        .attr("id", imgId)
                        .attr("xlink:href", "Images/icons/ajax-loader.gif")
                        .attr("x", "+16px")
                        .attr("y", "-16px")
                        .attr("width", "20px")
                        .attr("height", "20px");
                    } else if (d.state == "1") {
                        var imgId = "#" + d.name;
                        d3.select(imgId).remove();
                    } else if (d.state == "3" || d.state == "4") {
                        //d3.select(this)
                        //    .style("opacity", 0.4);

                        d3.select(this)
                        .style("filter", function (d, i) {
                            if (i % 2 == 0) {
                                return ("filter", "url(#desaturate)");
                            } else {
                                return "";
                            }
                        });
                    }

                    d3.select(this).append("text")
                    .attr("dy", ".50em")
                    .attr("text-anchor", "end")
                    .attr("font-size", "15px")
                    .attr("fill", "black")
                    .text(function (d) { return d.name; });

                    d3.select(this).call(force.drag);
                })
                //.call(force.drag)
                .on('mouseover', tip.show)
                .on('mouseout', tip.hide);

                // Delete removed nodes
                node.exit().remove();

                //debugger;
                force.start();
            }

I understand that right now, its iterating over new nodes only. Can someone please tell me how to iterate over the existing nodes too?

Thanks in advance.

Answer

Andrew picture Andrew · Dec 10, 2014

You can simply select your nodes and manipulate them in the part of your code where the state update happens.

function updateState() {
  ... // processing the update
  d3.selectAll('g.node')  //here's how you get all the nodes
    .each(function(d) {
      // your update code here as it was in your example
      d3.select(this) // Transform to d3 Object
      ... 
    });
}

If the update happens very frequently, it might be worth storing the selection in a variable and saving the time it takes to search through the DOM.