Skip to content Skip to sidebar Skip to footer

D3.js Change Width Of Container After It Is Drawn

I have a seemingly simple d3.js problem. I am creating a tree from a set of json data. The tree is composed of labels that are composed of a rectangle container that wrap around so

Solution 1:

This is the current structure of your nodes:

<g><rect></rect><text></text></g>

That being the case, the texts are the nextSiblings of the rectangles. Therefore, all you need to get the length of the texts is using nextSibling in the rectangle selection:

nodeRect.attr("width", function() {
    returnthis.nextSibling.getComputedTextLength() + rectW
})

Here I'm adding rectW to keep the same padding on the left and right, since you're putting the texts to start at rectW / 2.

If you're not sure about the relationship between the texts and rectangles (who is the first child, who is the last child...), you can go up, select the group element and then select the text inside it:

nodeRect.attr("width", function() {
    return d3.select(this.parentNode).select("text").node().getComputedTextLength() + rectW
})

Here is a basic demo:

var data = [{
    name: "some text",
    x: 10,
    y: 10
  },
  {
    name: "A very very very long text here",
    x: 100,
    y: 50
  },
  {
    name: "Another text, this time longer than the previous one",
    x: 25,
    y: 100
  },
  {
    name: "some short text here",
    x: 220,
    y: 150
  }
];

var svg = d3.select("svg");

var rectW = 140,
  rectH = 30;

var node = svg.selectAll(null)
  .data(data);

var nodeLabel = node.enter().append('g')
  .attr('transform', function(d) {
    return'translate(' + d.x + ',' + d.y + ')';
  });

var nodeRect = nodeLabel.append('rect')
  .attr('width', rectW)
  .attr('height', rectH)
  .style("fill", "none")
  .style("stroke", "gray")

var nodeText = nodeLabel.append('text')
  .attr('x', rectW / 2)
  .attr('y', rectH / 2)
  .style("dominant-baseline", "central")
  .text(function(d) {
    return d.name;
  });

nodeRect.attr("width", function() {
  returnthis.nextSibling.getComputedTextLength() + rectW
})
<scriptsrc="https://d3js.org/d3.v5.min.js"></script><svgwidth="500"height="300"></svg>

For storing the computed width and using it later on, you can set another property. For instance:

nodeRect.attr("width", function(d) {
    return d.rectWidth = this.nextSibling.getComputedTextLength() + rectW
});

Here is the demo, look at the console:

var data = [{
    name: "some text",
    x: 10,
    y: 10
  },
  {
    name: "A very very very long text here",
    x: 100,
    y: 50
  },
  {
    name: "Another text, this time longer than the previous one",
    x: 25,
    y: 100
  },
  {
    name: "some short text here",
    x: 220,
    y: 150
  }
];

var svg = d3.select("svg");

var rectW = 140,
  rectH = 30;

var node = svg.selectAll(null)
  .data(data);

var nodeLabel = node.enter().append('g')
  .attr('transform', function(d) {
    return'translate(' + d.x + ',' + d.y + ')';
  });

var nodeRect = nodeLabel.append('rect')
  .attr('width', rectW)
  .attr('height', rectH)
  .style("fill", "none")
  .style("stroke", "gray")

var nodeText = nodeLabel.append('text')
  .attr('x', rectW / 2)
  .attr('y', rectH / 2)
  .style("dominant-baseline", "central")
  .text(function(d) {
    return d.name;
  });

nodeRect.attr("width", function(d) {
  return d.rectWidth = this.nextSibling.getComputedTextLength() + rectW
});

nodeLabel.each(function(d) {
  console.log(d)
})
<scriptsrc="https://d3js.org/d3.v5.min.js"></script><svgwidth="500"height="300"></svg>

Solution 2:

You can add the following line at the end of your script, after the text has been set:

nodeRect
.transition()
.attr("width",function(){
return Math.max(rectW,nodeText.node().getComputedTextLength())
}).delay(0).duration(500)

I used Math.max to set it to rectW if its large enough or expand if necessary. You can perhaps adapt that part.

Post a Comment for "D3.js Change Width Of Container After It Is Drawn"