1

Recently I tried mastering the d3js library (which is great) and I am trying to do the easiest stuff but for some reason it doesn't work correctly at all. I did look at related issues but none could help me to find my problem.

The setup is simple, I have a force layout with only one node and no link. When the user clicks on that node, I would like to add a new node that gets linked to the node that was clicked. Here is my code so far. The first added node has a very random position and after that I keep having a message "Uncaught TypeError: Cannot read property '__data__' of null".

Thank you for your help !

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>D3 test</title>
</head>
<body>
    <div id="viz">
    </div>
    <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script type="text/javascript">
        var w = 800;
        var h = 800;
        var padding = 100;
        var svg = d3.select("#viz")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);
        var nodes = [{name:"n0"}];
        var links = []; 
        var force = d3.layout.force()
                        .size([w, h])
                        .linkDistance([100])
                        .charge([-30])
                        .start();
        var link = svg.selectAll(".link"),
            node = svg.selectAll(".node");

        update();

        function update() {
            force.nodes(nodes)
                  .links(links)
                  .start();
            link = link.data(force.links(), function(d) {return d.source.name + "-" + d.target.name;})
                            .enter()
                            .insert("line", ".node")
                            .attr("class", "link")
                            .style("stroke", "red")
                            .style("stroke-width", 2);

            node = node.data(force.nodes(), function(d) {return d.name;})
                            .enter()
                            .append("circle")
                            .attr("class", "node")
                            .attr("r", 10)
                            .style("fill", "black")
                            .on("click", function(d) {
                                var n = {name:"n"+nodes.length};
                                nodes.push(n);
                                links.push({source:d, target:n});
                                update();
                            })
                            .call(force.drag);
        }
        force.on("tick", function() {
            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("cx", function(d) {return d.x})
                    .attr("cy", function(d) {return d.y});
        });
    </script>
</body>

2
  • 3
    This example does almost exactly what you want. On a general note, the force layout is not the best way to get started with D3. Stick to the easy stuff first :) Commented Jul 2, 2013 at 14:10
  • Ok thanks for the advice ! I can see why your example works, still can't see what's faulty in mine though... If you have any idea I'd love to know because it's really bugging me :) Commented Jul 2, 2013 at 14:15

1 Answer 1

2

For a reason that I can't explain, it works perfectly when I change this bit

node = node.data(force.nodes(), function(d) {return d.name;}).enter()...

to this

node = node.data(force.nodes(), function(d) {return d.name;})
node.enter()...

And do the same to the "link" selection

I guess it has to do with enter selections and co.

Sign up to request clarification or add additional context in comments.

1 Comment

The reason is that in tick, with the first version you use the entered selection (new nodes only) whereas on the second version you use the whole selection (all nodes).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.