After being inspired by this link, I successfully created a basic network visualization app using D3.js.
The app reads node names from a textarea on an HTML page and then constructs a network where all nodes are interconnected.
All the relevant code can be found at the end of this post.
However, my current challenge lies in setting labels for each node. Upon loading the D3jNetVis.html file in a web browser, I encounter the following error:
Uncaught TypeError: D3jNetVis.js:64
undefined is not a function
This error occurs when trying to set labels with the following piece of code:
dataSet.nodes.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
The suggestion to implement node labels came from a thread on Stack Overflow.
I'm seeking assistance to understand why this error is happening and how it can be resolved.
Google hinted that the issue could be related to the order in which JS files are imported in the HTML page. Despite experimenting with different combinations, I haven't been able to fix it.
Regards,
Erno Lindfors
D3jNetVis.html:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Network Visualization Example - d3js</title>
<link rel="stylesheet" type="text/css" href="D3jNetVis.css">
</head>
<body>
<table>
<tr><td><div id="svgContent"></div></td></tr>
<tr><th align="left">Give node ids</th></tr>
<tr><td><textarea id="nodeIds" cols=5 rows=20></textarea></td></tr>
<tr><td><button type="button" onclick="constNet()">Construct Network</button></td></tr>
</table>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="D3jNetVis.js" charset="utf-8"></script>
</body>
</html>
D3jNetVis.js:
function constNet() {
var textArea = document.getElementById("nodeIds");
var nodeIdsArray = document.getElementById("nodeIds").value.split("\n");
var w = 500,
h = 500;
var svg = d3.select("#svgContent")
.append("svg")
.attr("width", w)
.attr("height", h)
.attr('preserveAspectRatio', 'xMinYMin slice')
.append('g');
var nodesArray = [];
for (var i = 0; i < nodeIdsArray.length; i++) {
var nodeId = nodeIdsArray[i];
var newNode = {name: "Node" + nodeId, id:nodeId, fixed:false};
nodesArray[nodesArray.length] = newNode;
}
var edgesArray = [];
for (var i = 0; i < nodeIdsArray.length-1; i++) {
var sNodeId = nodeIdsArray[i];
for (var j = i+1; j < nodeIdsArray.length; j++) {
var tNodeId = nodeIdsArray[j];
edgesArray[edgesArray.length] = {source:sNodeId-1, target:tNodeId-1};
}
}
var dataSet = {
nodes: nodesArray,
edges: edgesArray
};
var force = self.force = d3.layout.force()
.nodes(dataSet.nodes)
.links(dataSet.edges)
.gravity(0.05)
.distance(100)
.charge(-100)
.size([w,h])
.start();
var link = svg.selectAll(".link")
.data(dataSet.edges)
.enter().append("line")
.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; });
var node_drag = d3.behavior.drag()
.on("dragstart", dragstart)
.on("drag", dragmove)
.on("dragend", dragend);
var node = svg.selectAll("circle")
.data(dataSet.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 4.5)
.call(node_drag);
/*
The "Uncaught TypeError" happens in the next line.
*/
dataSet.nodes.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
function dragstart(d, i) {
force.stop();
}
function dragmove(d, i) {
d.px += d3.event.dx;
d.py += d3.event.dy;
d.x += d3.event.dx;
d.y += d3.event.dy;
tick();
}
function dragend(d, i) {
d.fixed = true;
tick();
force.resume();
}
force.on("tick", tick);
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("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
}
D3jNetVis.css:
line{
stroke: #cccccc;
stroke-width: 1;
}
circle{
fill: blue;
}