1

How can I wrap text inside each node making the node automatically resize, just as in css you would do with fit-content or display flex?

Here's the snippet, available at this link: https://codepen.io/boxxello/pen/XWYvRQO

or here: css:

html, body {
    margin: 0;
    padding: 0;
    background-color: rgb(140, 149, 226);
}
.node {
    cursor: pointer;
}

.node circle {
}
.node-leaf {


}

.node text {
    font: 10px sans-serif;
}

.link {
    fill: none;
    stroke: rgb(55, 68, 105);
    stroke-width: 1px;
}

js:


var margin = {top: 20, right: 90, bottom: 30, left: 90},
    width = 1300 - margin.left - margin.right,
    height = 900 - margin.top - margin.bottom;
var svg = d3
    .select("body")
    .append("svg")
    .attr("width", width + margin.right + margin.left)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var i = 0,
    duration = 750,
    root;
var treemap = d3.tree().size([height, width]);
root = d3.hierarchy(treeData, function (d) {
    return d.children;
});
root.x0 = height / 2;
root.y0 = 0;
root.children.forEach(collapse);


update(root);

function collapse(d) {
    if (d.children) {
        d._children = d.children;
        d._children.forEach(collapse);
        d.children = null;
    }
}


function update(source) {
    var treeData = treemap(root);
    var nodes = treeData.descendants(),
        links = treeData.descendants().slice(1);
    nodes.forEach(function (d) {
        d.y = d.depth * 180;
    });
    var node = svg.selectAll("g.node").data(nodes, function (d) {
        return d.id || (d.id = ++i);
    });
    var nodeEnter = node
        .enter()
        .append("g")
        .attr("class", "node")
        .attr("transform", function (d) {
            return "translate(" + source.y0 + "," + source.x0 + ")";
        })
        .on("click", click);
    nodeEnter
        .attr("class", "node")
        .attr("r", 1e-6)
        .style("display", function (d) {
            if (d.depth === 0) { //Is top root
                return 'none';
            }
        });
    nodeEnter
        .append("rect")
        .attr("rx", function (d) {
            if (d.parent) return d.children || d._children ? 0 : 6;
            return 10;
        })
        .attr("ry", function (d) {
            if (d.parent) return d.children || d._children ? 0 : 6;
            return 10;
        })
        .attr("stroke-width", function (d) {
            return d.parent ? 1 : 0;
        })
        .attr("stroke", function (d) {
            return d.children || d._children
                ? "rgb(3, 192, 220)"
                : "rgb(38, 222, 176)";
        })
        .attr("stroke-dasharray", function (d) {
            return d.children || d._children ? "0" : "2.2";
        })
        .attr("stroke-opacity", function (d) {
            return d.children || d._children ? "1" : "0.6";
        })
        .attr("x", 0)
        .attr("y", -10)
        .attr("width", function (d) {
            return d.parent ? 40 : 20;
        })
        .attr("height", 20)
        .classed("node-leaf", true);

    nodeEnter
        .append("text")
        .style("fill", function (d) {
            if (d.parent) {
                return d.children || d._children ? "#ffffff" : "rgb(38, 222, 176)";
            }
            return "rgb(39, 43, 77)";
        })
        .attr("dy", ".35em")
        .attr("x", function (d) {
            return d.parent ? 20 : 10;
        })
        .attr("text-anchor", function (d) {
            return "middle";
        })
        .text(function (d) {
            return d.data.name;
        });

    var nodeUpdate = nodeEnter.merge(node);

    nodeUpdate
        .transition()
        .duration(duration)
        .attr("transform", function (d) {
            return "translate(" + d.y + "," + d.x + ")";
        });
    var nodeExit = node
        .exit()
        .transition()
        .duration(duration)
        .attr("transform", function (d) {
            return "translate(" + source.y + "," + source.x + ")";
        })
        .remove();
    nodeExit.select("rect").style("opacity", 1e-6);
    nodeExit.select("rect").attr("stroke-opacity", 1e-6);
    nodeExit.select("text").style("fill-opacity", 1e-6);
    var link = svg.selectAll("path.link").data(links, function (d) {
        return d.id;
    });
    var linkEnter = link
        .enter()
        .insert("path", "g")
        .attr("class", "link")
        .attr("d", function (d) {
            var o = {x: source.x0, y: source.y0};
            return diagonal(o, o);
        }).style("display", function (d) {
            if (d.depth === 1) { //Is top link
                return 'none';
            }
        })
    var linkUpdate = linkEnter.merge(link);
    linkUpdate
        .transition()
        .duration(duration)
        .attr("d", function (d) {
            return diagonal(d, d.parent);
        });
    var linkExit = link
        .exit()
        .transition()
        .duration(duration)
        .attr("d", function (d) {
            var o = {x: source.x, y: source.y};
            return diagonal(o, o);
        })
        .remove();
    nodes.forEach(function (d) {
        d.x0 = d.x;
        d.y0 = d.y;
    });

    function diagonal(s, d) {
        path = `M ${s.y} ${s.x}
            C ${(s.y + d.y) / 2} ${s.x},
              ${(s.y + d.y) / 2} ${d.x},
              ${d.y} ${d.x}`;

        return path;
    }

    function click(d) {
        if (d.children) {
            d._children = d.children;
            d.children = null;
        } else {
            d.children = d._children;
            d._children = null;
        }
        update(d);
    }
}

The part where you could change the style property is at:

        .attr("width", function (d) {
            return d.parent ? 40 : 20;
        })
        .attr("height", 20)
        .classed("node-leaf", true);

Or at least is where I've tried to deal with it. The current version I'm using of d3js is the 4.2.2, I actually used this version it fitted my needs (found the snippet online and there have been breaking changes in the 7.* which is the current version) not because I didn't want to upgrade it (if it's easier in the newer versions please let me know.

This is an expected output, as you can see the node basically fits all the text inside it (I left the parent nodes blank because they're not relevant in this case, but they would need to do the same thing, so applying a property to all the nodes would be great.

enter image description here

halfer
  • 19,824
  • 17
  • 99
  • 186
  • I'm curious about this too. The limitation with D3 is all graphics are inside one single svg element so not many styling options are available. I found this: https://stackoverflow.com/questions/24784302/wrapping-text-in-d3 injecting an element around each bit of text to then control its wrapping behaviour. – Nathaniel Flick Dec 15 '22 at 23:27

0 Answers0