18

I'm trying to get the widths of a bunch of text elements I have created with d3.js

This is how I'm creating them:

var nodesText = svg.selectAll("text")
           .data(dataset)
           .enter()
           .append("text")
           .text(function(d) {
                return d.name;
           })
          .attr("x", function(d, i) {
                return i * (w / dataset.length);
           })
          .attr("y", function(d) {
                return 45;
          });

I'm then using the width to create rectangles the same size as the text's boxes

var nodes = svg.selectAll("rect")
            .data(dataset)
            .enter()
            .append("rect")
            .attr("x", function(d, i) {
                return i * (w / dataset.length);
            })
            .attr("y", function(d) {
                return 25;
            })
            .attr("width", function(d, i) {
              //To Do: find width of each text element, after it has been generated
              var textWidth = svg.selectAll("text")
                  .each(function () {
                      return d3.select(this.getComputedTextLength());
                  });
                console.log(textWidth);
                return textWidth;
            })
            .attr("height", function(d) {
                return 30;
            })

I tried using the Bbox method from here but I don't really understand it. I think selecting the actual element is where I'm going wrong really.

Community
  • 1
  • 1
suryanaga
  • 3,728
  • 11
  • 36
  • 47

3 Answers3

15

I would make the length part of the original data:

var nodesText = svg.selectAll("text")
       .data(dataset)
       .enter()
       .append("text")
       .text(function(d) {
            return d.name;
       })
      .attr("x", function(d, i) {
            return i * (w / dataset.length);
       })
      .attr("y", function(d) {
            return 45;
      })
      .each(function(d) {
        d.width = this.getBBox().width;
      });

and then later

var nodes = svg.selectAll("rect")
        .data(dataset)
        .enter()
        .append("rect")
        .attr("width", function(d) { return d.width; });
Lars Kotthoff
  • 107,425
  • 16
  • 204
  • 204
  • what is the 'w' in (w / dataset.length)? I looked at [svg text](http://www.w3.org/TR/SVG/text.html#TextElement) and [d3 text](https://github.com/mbostock/d3/wiki/Selections#text) and cannot find what 'w' is – subsci Jul 16 '15 at 20:59
  • The total width you set for the text. – Lars Kotthoff Jul 16 '15 at 22:17
4

You can use getBoundingClientRect()

Example:

.style('top', function (d) {
     var currElemHeight = this.getBoundingClientRect().height;
}

edit: seems like its more appropriate for HTML elements. for SVG elements you can use getBBbox() instead.

Liran Brimer
  • 3,418
  • 1
  • 28
  • 23
0

d3.selectAll returns a selection. You can get each of the elements by navigating through the array in the _groups property. When you are determining the width of a rectangle, you can use its index to get the corresponding text element:

.attr('width', function (d, i) {
    var textSelection = d3.selectAll('text');
    return textSelection._groups[0][i].getComputedTextLength();
});

The _groups property of d3's selection has a list of nodes at [0]. This list contains all of the selected elements, which you can access by index. It's important that you get the SVG element so that you can use the getComputedTextLength method.

You may also want to consider creating the rect elements first, then the text elements, and then going back to the rectangles to edit the width attribute, so that the text elements are on top of the rectangles (in case you want to fill the rectangles with color).

Update:

It's typically preferred that you don't access _groups, though, so a safer way to get the matching text element's width would be:

.attr('width', function (d, i) {
    return d3.selectAll('text').filter(function (d, j) { return i === j; })
        .node().getComputedTextLength(); 
});

Using node safely retrieves the element, and filter will find the text element which matches index.

M. Davis
  • 669
  • 2
  • 10
  • 17