Question

I'm trying to implement the expand / collapse functionality as i saw on the D3 site, and what i get is that some nodes get to have multiple labels when i run this functionality.

   var width = 960,
            height = 500;

        var node,
            path,
            root, nodes, links, link;

        var svg = d3.select("body").append("svg")
            .attr("width", width)
            .attr("height", height);

        var force = d3.layout.force();

        var resource = $.getJSON('js/nodes.json', function(data)
        {
            root = restructure(data);
            render();
        })

        function restructure(data)
        {
            var root = data[0];
            data.splice(0,1);
            root.children = data;
            _.each(root.children, function(child)
            {
                child._children = child.links;
            })

            return root;
        }


        function flatten(root) {

            // return root;
            var nodes = [];
            var links = [];

            function rec(node) {
                var sourceNode = addNode(nodes, node);

                if (node.children)
                {
                    //sourceNode.children = [];
                    node.children.forEach(function(child)
                    {
                        var targetNode = rec(child);
                        links.push({source:sourceNode, target:targetNode});
                        //sourceNode.children.push(targetNode);
                    })

                }

                return sourceNode;
            }


            rec(root);
            return {nodes:d3.values(nodes), links:links};
        }

        function addNode(collection, node) {
            if(collection[node.name] != null)
                return collection[node.name];

            collection[node.name] = node;
            return node;
        }

        function render()
        {
            var flat = flatten(root);
            var nodes = flat.nodes;
            var links = flat.links;

             nodes = tree.nodes(elements.nodes).reverse();
             links = tree.links(nodes);*/

            force.nodes(nodes)
                .links(links)
                .size([width, height])
                .linkDistance(160)
                .charge(-1500)
                .on("tick", tick)
                .start();

            var drag = force.drag()
                .on("dragstart", dragstart);

            function dragstart(d) {
                d.fixed = true;
                d3.select(this).classed("fixed", true);
            }

            function isRoot(node)
            {
                return node.id == root.id
            }

            /*  link = svg.selectAll(".link")
             .data(force.links())
             .enter().append("line")
             .attr("class", "link");*/


            // Update the links…
            link = svg.selectAll("line.link")
                .data(force.links(), function(d) { return d.target.id; });

            // Enter any new links.
            link.enter().insert("line", ".node")
                .attr("class","link")
                .attr("x1", function(d) { return d.source.x; })
                .attr("y1", function(d) { return d.source.y; })
                .attr("x2", function(d) { return d.target.x; })
                .attr("y2", function(d) { return d.target.y; })
                .style("stroke-width",function(l)
                {
                    console.log(l.source.rank + ':' + l.target.rank);
                    var val = Math.min(10,Math.max(1,Math.log(Math.max(Math.abs(l.source.rank),Math.abs(l.target.rank)))));
                    console.log('width: ' + val);
                    return val;
                });

            // Exit any old links.
            link.exit().remove();


            node = svg.selectAll(".node")
                .data(force.nodes());

            node.exit().remove();

            node.enter().append("g")
                .attr("class", "node")
                .attr("id", function(d) { return d.id; })
                .on("click", click)
                .on("dblclick", doubleclick)
                .on("mouseover", mouseover)
                .on("mouseout", mouseout)
                .call(force.drag);

            node.append("image")
                .attr("xlink:href", function (d)
                {
                    return "../img/icon-location.png";
                })
                .attr("x", -8)
                .attr("y", -8)
                .attr("width", 16)
                .attr("height", 16);

            /*  node.append("circle")
             .attr("r", function(d)
             {
             return isRoot(d)? 14:8;
             })
             .style("fill",function(d)
             {
             return isRoot(d)? "steelblue":"";
             });*/

            var center = svg.select('#node_' + nodes[0].id)
            center.append("circle")
                .attr("r", "14")
                .style("fill","steelblue");

            node.append("text")
                .attr("x", 12)
                .attr("dy", ".35em")
                .text(function(d) { return  d.name; });



            node.transition()
                .attr("r", function(d) { return d.children ? 4.5 : Math.sqrt(d.size) / 10; });

            // Exit any old nodes.
            node.exit().remove();
        }

        function tick() {
            link
                .attr("x1", function(d) { return d.source.x; })
                .attr("y1", function(d) { return d.source.y; })
                .attr("x2", function(d) { return d.target.x; })
                .attr("y2", function(d) { return d.target.y; });

            node
                .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
        }

        // Toggle children on click.
        function doubleclick(d) {
            alert(d);
        }
        // Toggle children on click.
        function click(d) {
            if (d.children) {
                d._children = d.children;
                d.children = null;
            } else {
                d.children = d._children;
                d._children = null;
            }
            render();
        }

        function mouseover() {
            d3.select(this).select("circle").transition()
                .duration(750)
                .attr("r", 16);
        }

        function mouseout() {
            d3.select(this).select("circle").transition()
                .duration(750)
                .attr("r", 8);
        }

however, what i get (after expanding) looks like that:

enter image description here

<g class="node fixed" id="1063" transform="translate(329.44373878271944,118.27604414379978)" r="NaN"><image xlink:href="../img/icon-location.png" x="-8" y="-8" width="16" height="16"></image><text x="12" dy=".35em">PRO</text><image xlink:href="../img/icon-location.png" x="-8" y="-8" width="16" height="16"></image><text x="12" dy=".35em">Dropbox</text></g>

any help?

Was it helpful?

Solution

You want to make sure that updated nodes are correctly matched to the existing DOM elements, which you're already doing for the edges. You'll pass an identity function as the second parameter to the node data() call. Perhaps:

        node = svg.selectAll(".node")
            .data(force.nodes(), function(d) { return d.id; });

The one thing you need to look out for is that this function can be called both on new data elements as well as the stored data on nodes being updated.

For more information see http://bost.ocks.org/mike/selection/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top