Dash
Dash

Reputation: 11

Dynamically create nodes in D3.js

Note: My first StackOverFlow post, please bear with me.

Aim of my code:

Approach:

I have decided to use D3.js for the purpose. I am a newbie in JavaScript/jQuery but still I learnt D3.js from https://www.dashingd3js.com/table-of-contents . I have seen people create amazingly beautiful projects involving graph using D3.js but I am kind of struggling even in the basics.

Attempt:

I wrote the following code to connect two nodes:

<!DOCTYPE html>
<html>

<head>
<title>D3 Try</title>
<script type="text/javascript" src="d3.min.js"></script>

</head>

<style>

.node {
    fill: #ccc;
    stroke: #fff;
    stroke-width: 2px;
}

.link {
    stroke: #777;
    stroke-width: 2px;
}

    </style>
<body style="background-color: red">

<h2>Hello world</h2>

<script type="text/javascript">

var height=500,width=960;

// If we leave  out the x and y coordinates of the visualization, d3 selects 
// random positions for the nodes

// nodes are arbitrary objects

var nodes = [ {x: width/3, y: height/2 } , {x: 2*width/3, y: height/2} ];

// Define the array which contains information about the links

var links= [ {source: 0, target: 1}];


// container to hold the visualisation

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

// Create force layout object which contains dimensions of the container and 
// the arrays of nodes and links


var force=d3.layout.force().size([width,height]).nodes(nodes).links(links);



// Documentation says that define a function in place of width/2 but fir bhi , how?


force.linkDistance(width/2);



var link=svg.selectAll('.link').data(links).enter().append('line').attr('class','link');

var node=svg.selectAll('.node').data(nodes).enter().append('circle').attr('class','node');



force.on('end',function(){ 




node.attr('r', width/25)
        .attr('cx', function(d) { return d.x; })
        .attr('cy', function(d) { return d.y; });



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; });


});


force.start();








</script>





</body>

</html>

Problems I faced and what I really want to ask:

  1. The above code connects two nodes, okay great, but how do I dynamically create such nodes on user's double click ? It may here be noted that not only a node has to be drawn on SVG, but the nodes and links arrays are to be updated too. How do I update those hard-coded arrays dynamically so that they store the most recent information in them?

  2. The distance between the two nodes must be entered by the user, here it is a constant width/2 . I checked out the API on github, they say that define a function instead of a constant.I searched but couldn't find any example of that. Even if I use the D3 supplied variable d, its of no use since it has to be user dependent.

Any help? Thank You.

Upvotes: 1

Views: 4179

Answers (1)

ckersch
ckersch

Reputation: 7687

To do this, you can rely on the magic of the d3 .enter() method. This is a data that operates on a selection, and returns a placeholder for every piece of data assigned to the selection that doesn't currently map to an SVG element in the DOM.

What this means is that if you modify the data, you can then append that data to a selection and have any changes in the data represented by changes in the selection. If you want to add a node to your data, it would work like this:

//Modify data
nodes.push({x : d3.mouse(svg)[0], y : d3.mouse(svg)[1]});

//Modify data of node selection
node.data(nodes);

//Operate on new nodes
node.enter()
  .append('circle')
  .attr('class','node')
  .attr('r', width/25)
  .attr('cx', function(d) { return d.x; })
  .attr('cy', function(d) { return d.y; });
  .call(force.end);

Since your first two data points already have DOM nodes assigned to them from when you first created them, this will only append a new circle element for the new data point.

All of this would be called from within an event handler for mousedown or click, and would take user input in the form of mouse position.

Upvotes: 1

Related Questions