Trouble converting this D3 to version 4

Hey all, I’ve been working through this book:


But getting to the force charts there have been some significant changes in how it’s applied in v4. I haven’t had too much trouble converting other elements but this one has me stumped, anyone know how to convert this to work in v4?

The code:

          //Width and height
          var w = 500;
          var h = 300;

          //Original data
          var dataset = {
            nodes: [
              { name: "Adam" },
              { name: "Bob" },
              { name: "Carrie" },
              { name: "Donovan" },
              { name: "Edward" },
              { name: "Felicity" },
              { name: "George" },
              { name: "Hannah" },
              { name: "Iris" },
              { name: "Jerry" }
            ],
            edges: [
              { source: 0, target: 1 },
              { source: 0, target: 2 },
              { source: 0, target: 3 },
              { source: 0, target: 4 },
              { source: 1, target: 5 },
              { source: 2, target: 5 },
              { source: 2, target: 5 },
              { source: 3, target: 4 },
              { source: 5, target: 8 },
              { source: 5, target: 9 },
              { source: 6, target: 7 },
              { source: 7, target: 8 },
              { source: 8, target: 9 }
            ]
          };

          //Initialize a default force layout, using the nodes and edges in dataset
          var force = d3.forceSimulation()
                     .nodes(dataset.nodes)
                     .links(dataset.edges)
                     .size([w, h])
                     .linkDistance([50])
                     .charge([-100]);

          var colors = d3.scaleOrdinal(d3.schemeCategory10);

          //Create SVG element
          var svg = d3.select("body")
                .append("svg")
                .attr("width", w)
                .attr("height", h);

          //Create edges as lines
          var edges = svg.selectAll("line")
            .data(dataset.edges)
            .enter()
            .append("line")
            .style("stroke", "#ccc")
            .style("stroke-width", 1);

          //Create nodes as circles
          var nodes = svg.selectAll("circle")
            .data(dataset.nodes)
            .enter()
            .append("circle")
            .attr("r", 10)
            .style("fill", function(d, i) {
              return colors(i);
            })
            .call(force.drag);

          //Every time the simulation "ticks", this will be called
          force.on("tick", function() {

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

            nodes.attr("cx", function(d) { return d.x; })
               .attr("cy", function(d) { return d.y; });

          });

Hi, jeffm64

here is your code in D3.v4 version.
I left your code in comments to better understanding. I hope it will not be incomprehensible.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Directed Graph</title>
    <script src='https://d3js.org/d3.v4.min.js'></script>
    <script src='http://code.jquery.com/jquery-3.1.1.min.js' integrity='sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=' crossorigin='anonymous'></script>
    <style>
        .links line {
            stroke: #aaa;
        }

        .nodes circle {
            pointer-events: all;
            stroke: none;
            stroke-width: 40px;
        }
    </style>
</head>
<body>
<svg></svg>
<script>
    //Width and height
    var svgWidth = 800;
    var svgHeight = 300;
    var circleRadius = 10;
    //Original data
    var dataset = {
        nodes: [
            { name: "Adam" },
            { name: "Bob" },
            { name: "Carrie" },
            { name: "Donovan" },
            { name: "Edward" },
            { name: "Felicity" },
            { name: "George" },
            { name: "Hannah" },
            { name: "Iris" },
            { name: "Jerry" }
        ],
        edges: [
            { source: 0, target: 1 },
            { source: 0, target: 2 },
            { source: 0, target: 3 },
            { source: 0, target: 4 },
            { source: 1, target: 5 },
            { source: 2, target: 5 },
            { source: 2, target: 5 },
            { source: 3, target: 4 },
            { source: 5, target: 8 },
            { source: 5, target: 9 },
            { source: 6, target: 7 },
            { source: 7, target: 8 },
            { source: 8, target: 9 }
        ]
    };

    //Initialize a default force layout, using the nodes and edges in dataset
    //var force = d3.forceSimulation()
    //            .nodes(dataset.nodes)
    //            .links(dataset.edges)
    //            .size([w, h])
    //            .linkDistance([50])
    //            .charge([-100]);

    var simulation = d3.forceSimulation()
            .force("link", d3.forceLink().id(function(d,i) {
                return i;
            }).distance(50))
            .force("charge", d3.forceManyBody().strength(-100 ))
            .force("center", d3.forceCenter(svgWidth / 2,svgHeight / 2));

    var colors = d3.scaleOrdinal(d3.schemeCategory10);

    //Create SVG element
    var svg = d3.select("svg")
            .attr("width", svgWidth)
            .attr("height", svgHeight);

    //Create edges as lines
    //    var edges = svg.selectAll("line")
    //            .data(dataset.edges)
    //            .enter()
    //            .append("line")
    //            .style("stroke", "#ccc")
    //            .style("stroke-width", 1);
    var edges = svg.append('g')
            .attr('class','links')
            .selectAll("line")
            .data(dataset.edges)
            .enter()
            .append("line");

    //Create nodes as circles
    //    var nodes = svg.selectAll("circle")
    //            .data(dataset.nodes)
    //            .enter()
    //            .append("circle")
    //            .attr("r", 10)
    //            .style("fill", function(d, i) {
    //                return colors(i);
    //            })
    //            .call(force.drag);
    var node = svg.append('g')
            .attr('class','nodes')
            .selectAll('circle')
            .data(dataset.nodes)
            .enter()
            .append("circle")
            .attr("r", circleRadius)
            .attr('fill',function (d,i) {
                return colors(i)
            })
            .call(d3.drag()
                    .on("start", dragstarted)
                    .on("drag", dragged)
                    .on("end", dragended));

    var nodes_text = svg.selectAll(".nodetext")
            .data(dataset.nodes)
            .enter()
            .append("text")
            .attr("class", "nodetext slds-text-heading--label")
            .attr("text-anchor", "middle")
            .attr("dx", -20)
            .attr("dy", 20)
            .text(function(d) {
                return  d.name;
            });
    //Every time the simulation "ticks", this will be called
    //    force.on("tick", function() {
    //
    //        edges.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; });
    //
    //        nodes.attr("cx", function(d) { return d.x; })
    //                .attr("cy", function(d) { return d.y; });
    //
    //    });
    simulation
            .nodes(dataset.nodes)
            .on("tick", ticked);
    simulation
            .force("link")
            .links(dataset.edges);
    function ticked() {
        edges
                .attr("x1", function (d) {
                    var xPos = d.source.x;
                    if (xPos < 0) return 0;
                    if (xPos > (svgWidth - circleRadius)) return (svgWidth - circleRadius);
                    return xPos;
                })
                .attr("y1", function (d) {
                    var yPos = d.source.y;
                    if (yPos < 0) return 0;
                    if (yPos > (svgHeight - circleRadius)) return (svgHeight - circleRadius);
                    return yPos;
                })
                .attr("x2", function (d) {
                    var xPos = d.target.x;
                    if (xPos < 0) return 0;
                    if (xPos > (svgWidth - circleRadius)) return (svgWidth - circleRadius);
                    return xPos;
                })
                .attr("y2", function (d) {
                    var yPos = d.target.y;
                    if (yPos < 0) return 0;
                    if (yPos > (svgHeight - circleRadius)) return (svgHeight - circleRadius);
                    return yPos;
                });

        node
                .attr("cx", function (d) {
                    var xPos = d.x;
                    if (xPos < 0) return 0;
                    if (xPos > (svgWidth - circleRadius)) return (svgWidth - circleRadius);
                    return xPos;
                })
                .attr("cy", function (d) {
                    var yPos = d.y;
                    if (yPos < 0) return 0;
                    if (yPos > (svgHeight - circleRadius)) return (svgHeight - circleRadius);
                    return yPos;
                });

        nodes_text
                .attr("x", function(d) {
                    var xPos = d.x;
                    if (xPos < 0) return 0;
                    if (xPos > (svgWidth - circleRadius)) return (svgWidth - circleRadius);
                    return xPos;
                })
                .attr("y", function(d) {
                    var yPos = d.y;
                    if (yPos < 0) return 0;
                    if (yPos > (svgHeight - circleRadius)) return (svgHeight - circleRadius);
                    return yPos;
                });
    }
    function dragstarted(d) {
        if (!d3.event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
    }

    function dragged(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
    }

    function dragended(d) {
        if (!d3.event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
    }

</script>

</body>
</html>

Hope it helps.
`

2 Likes

I know this an old post… in case someone stumbles upon this post…here is a reference for forced layouts in D3js version 4