0

I have implemented a simple network visualization app in D3.js by adopting ideas from http://jsbin.com/omokap/8/edit?html,css,js,output.

This application reads node names (separated by line breaks) from a textarea in an html page and then constructs a network in which all nodes are connected to each other.

All my codes are included in the end of this message.

My problem is that I cannot set labels to nodes.
More specifically I get the following error message when loading the D3jNetVis.html on a web browser.

Uncaught TypeError: D3jNetVis.js:64
undefined is not a function

This error happens when I try to set labels to nodes in the following code snippet:

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

This type of node label setter was suggested in D3.js, force-graph, cannot display text/label of nodes.

Any ideas why I get this error and how to fix it?

Google gave a hint that this could be related the importing order of the js-files in the html page but have been trying various combinations without success.

Thanks,
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(); // stops the force auto positioning before you start dragging
    }

    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; // of course set the node to fixed so the force doesn't include the node in its auto positioning stuff
        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;
}
Community
  • 1
  • 1

1 Answers1

0

You can just append html elements to a selection of html elements (so the browser knows where to append in the DOM).

dataSet.nodes is not a selection of html elements, that's why you get the error message.

Write instead: node.append("text").....

ee2Dev
  • 1,938
  • 20
  • 29
  • Had to make a few other additional tricks to get the node labels visible, mainly by adopting ideas from http://bl.ocks.org/mbostock/2706022. This issue is thus now solved. – Erno Lindfors Mar 18 '15 at 17:40