13

I want to write text inside a rectangle I create as follows:

body = d3.select('body')

svg = body.append('svg').attr('height', 600).attr('width', 200)

        rect = svg.append('rect').transition().duration(500).attr('width', 150)
                        .attr('height', 100)
                        .attr('x', 40)
                        .attr('y', 100)
                        .style('fill', 'white')
                        .attr('stroke', 'black')

        text = svg.append('text').text('This is some information about whatever')
                        .attr('x', 50)
                        .attr('y', 150)
                        .attr('fill', 'black')​

However, as you can see (http://jsfiddle.net/Tmj7g/3/) the text gets cut off. Any nifty ways to write a paragraph inside of the svg rectangle created? Thanks,

MarcoS
  • 13,386
  • 7
  • 42
  • 63
mike
  • 22,931
  • 31
  • 77
  • 100
  • This technique might come in handy. It works with the current svg spec and without foreignObject element http://bl.ocks.org/mbostock/7555321 – arunkjn Jul 23 '14 at 08:03

4 Answers4

16

The answer to this question might be relevant. SVG provides no way of wrapping text automatically, but you can embed HTML within SVGs and then use a div for example.

I've updated the jsfiddle here, but it doesn't work that well together with the animation. If you want to make it work properly and behave like any other SVG element, you'll have to pre-compute the line breaks and insert them manually.

Community
  • 1
  • 1
Lars Kotthoff
  • 107,425
  • 16
  • 204
  • 204
  • Does this work on IE9? If so what are the alternatives? I know this was answered long ago. – Jose Jun 04 '13 at 20:59
  • Yes, it doesnt work at all in IE9 because it doesnt support foreign Objects. Are there any alternatives to wrapping text in D3.js? – Jose Jun 04 '13 at 21:03
  • 1
    This is not so much a D3 but an SVG issue. If you don't need fancy graphics, you could simply generate HTML. If you need SVG, your can use its tspan elements to wrap text for example, but this is much less convenient. – Lars Kotthoff Jun 04 '13 at 21:08
14

To make it work with the animations just enclose in a group element and animate that one instead. http://jsfiddle.net/MJJEc/

body = d3.select('body')
svg = body.append('svg')
                    .attr('height', 600)
                    .attr('width', 200);
    var g = svg.append('g').attr("transform" ,"scale(0)");
    rect = g.append('rect')
                    .attr('width', 150)
                    .attr('height', 100)
                    .attr('x', 40)
                    .attr('y', 100)
                    .style('fill', 'none')
                    .attr('stroke', 'black')
    text = g.append('foreignObject')
                    .attr('x', 50)
                    .attr('y', 130)
                    .attr('width', 150)
                    .attr('height', 100)
                    .append("xhtml:body")
                    .html('<div style="width: 150px;">This is some information about whatever</div>')

    g.transition().duration(500).attr("transform" ,"scale(1)");
GusOst
  • 4,105
  • 3
  • 19
  • 28
2

I had a similar issue and found a reasonable solution by calculating the width of my box. Secondly, I figured out that on average the character width for my current font is about 8. Next I simply do a substring on the text to be displayed. That seems to work perfectly in most cases.

var rectText = rectangles.append("text")
    .text(function(d) {

        TextBoxLength = timeScale(dateFormat.parse(d.endTime)) - timeScale(dateFormat.parse(d.startTime));
        return d.task.substring(0, Math.floor(TextBoxLength / 8));
    })
    .attr("x", function(d) {
        return (timeScale(dateFormat.parse(d.endTime)) - timeScale(dateFormat.parse(d.startTime))) / 2 + timeScale(dateFormat.parse(d.startTime)) + theSidePad;
    })
    .attr("y", function(d, i) {
        return d.position * theGap + 14 + theTopPad;
    })
    .attr("font-size", 12)
    .attr("text-anchor", "middle")
    .attr("text-height", theBarHeight)
    .attr("fill", "#000000");
Gilsha
  • 14,431
  • 3
  • 32
  • 47
dirk
  • 21
  • 1
2

Another approach, when trying to fit a straight line of text into an svg element, could use the strategy found in http://bl.ocks.org/mbostock/1846692:

node.append("text")
  .text(function(d) { return d.name; })
  .style("font-size", function(d) { return Math.min(2 * d.r, (2 * d.r - 8) / this.getComputedTextLength() * 24) + "px"; })
  .attr("dy", ".35em");
maia
  • 3,910
  • 4
  • 27
  • 34