1

Lars Kotthof has a good explanation here of how to create SVG elements that correspond to size of text. However, I'm looking to do this dynamically with data pulled from JSON or CSV.

JS Fiddle here.

    svg.selectAll('rect')
       .data(states.features)
       .enter()
       .append('rect')
          .attrs({
            x: function(d) { return path.centroid(d)[0] - 50; },
            y: function(d) { return path.centroid(d)[1] - 13; },
            'text-anchor': 'middle',
            'width': 100,
            'height': 18,
            'fill': 'rgba(232, 232, 232, 0.8)',
            'opacity': 1,
            'rx': 7,
            'ry': 7
         }); 

    svg.selectAll('text')
       .data(states.features)
       .enter()
       .append('text')
          .text(function(d) { return d.properties.name; })
          .attrs({
            x: function(d) { return path.centroid(d)[0]; },
            y: function(d) { return path.centroid(d)[1]; },
            'text-anchor': 'middle',
            'font-size': '7pt',
            'fill': 'rgb(25,25,25)',
            'opacity': 1
         });   

The concept I'm not grasping is how I can write a function, similar to Lars's, that creates both the <rect> and the <text> and uses the dimensions of the text to determine the dimensions of the rectangle.

1 Answers1

3

Here's a solution and associated JS Fiddle. Basically what I did is assigned corresponding IDs to each rect and text, and then after the texts were generated, went adjusted the rect size based on the text. Additionally, the x position of the text had to be adjusted accordingly as well.

svg.selectAll('rect')
   .data(states.features)
   .enter()
   .append('rect')
      .attrs({
        y: function(d) { return path.centroid(d)[1] - 13; },
        'text-anchor': 'middle',
        'width': 100,
        'height': 18,
        'fill': 'rgba(232, 232, 232, 0.8)',
        'opacity': 1,
        'rx': 7,
        'ry': 7
     }); 

svg.selectAll('text')
   .data(states.features)
   .enter()
   .append('text')
      .text(function(d) { return d.properties.name; })
      .attrs({
        x: function(d) { return path.centroid(d)[0]; },
        y: function(d) { return path.centroid(d)[1]; },
        'text-anchor': 'middle',
        'font-size': '7pt',
        'fill': 'rgb(25,25,25)',
        'opacity': 1,
        id: function(d) { return 'text' + d.id }
     });   

svg.selectAll('rect')
    .attr('width', function(d) { return document.getElementById('text'+d.id).getBBox().width; })
    .attr('x', function(d) { return path.centroid(d)[0] - document.getElementById('text'+d.id).getBBox().width / 2; });
Nick
  • 16,066
  • 3
  • 16
  • 32
  • Thanks! This is a good solution to my question, as with the `filter` solution I did get the antialiasing issues. I'm curious what kind of impact this has on load time, but the method will be helpful for me in approaching new problems. – David Merritt Sep 18 '17 at 15:01