Limiting the width of a TEXT element

7,139 views
Skip to first unread message

Bob Monteverde

unread,
Nov 17, 2011, 1:40:24 PM11/17/11
to d3-js
I'm trying to limit the width of a text element (I'm using the
indented tree example, and trying to make columns that show additional
information, but the text in these may go beyond the space I have)

Things that don't work:

Setting width on the text element
Putting the text in a RECT that has a set width (SVG spec doesn't
allow text in a rectangle)

Anyone have a cool technique to get around this? (I have a string
shorten function that shortens a string until it fits into a given
width, but the performance on this is horrendous when doing it 200+
times on a single view)

The best I can think of right now is overlaying a rectangle at the end
of the string, so that the text hides behind it... but I really don't
like this idea.

Any advice is welcome.

Ian Johnson

unread,
Nov 17, 2011, 1:53:59 PM11/17/11
to d3...@googlegroups.com
You probably want to make a clippath with a rectangle that is the proper width and height

the other day someone posted to the mailing list a cool trick using gradients to fade out text past a certain point

not sure how that would scale performance wise.
--
Ian Johnson

Jon Frost

unread,
Nov 17, 2011, 2:00:30 PM11/17/11
to d3...@googlegroups.com
Yes, masking is what people tend to go with. Also there are the font
properties which help a bit: font-stretch, kerning, letter-spacing,
word-spacing, etc.

Just be sure to test that they perform correctly in the browsers,
because currently there is not a whole lot of consistency w/ regard to
the text and font properties as a whole.

This approach is also too bulky for your needs -
http://www.carto.net/papers/svg/textFlow/. From what I have heard,
text should be a lot easier to work with in svg 2.0 (n years from
now).

Jon

Bob Monteverde

unread,
Nov 17, 2011, 2:02:04 PM11/17/11
to d3-js
Thanks, I actually just finished implemented a clipPath with a
rectangle. Worked perfectly. Thanks for the response.

On Nov 17, 1:53 pm, Ian Johnson <enja...@gmail.com> wrote:
> You probably want to make a clippath with a rectangle that is the proper
> width and heighthttp://www.w3.org/TR/SVG/masking.html
>
> the other day someone posted to the mailing list a cool trick using
> gradients to fade out text past a certain pointhttp://groups.google.com/group/d3-js/browse_thread/thread/0f5140abf10...

Alex Simoes

unread,
Nov 17, 2011, 7:09:55 PM11/17/11
to d3...@googlegroups.com
Bob, 

Do you have an example of this posted anywhere, I am curious about this topic myself...

- alex

Kyle Foreman

unread,
Nov 18, 2011, 7:36:04 AM11/18/11
to d3...@googlegroups.com
Thanks for the link to textFlow, Jon, that's really useful! I had resorted to embedding divs in order to get proper wordwrap, but this looks like a better solution.

Chris Viau

unread,
Nov 19, 2011, 8:36:28 AM11/19/11
to d3...@googlegroups.com
Instead of embedding HTML divs, you could embed SVG tspan http://svg-whiz.com/svg/table.svg

Chris Viau

unread,
Nov 19, 2011, 8:43:02 AM11/19/11
to d3...@googlegroups.com
I sometimes crop my text according to the number of character. That's ugly but it's a good way to replace the cropped characters by "..." :

var croppedText = function(d, i){return (d.length > 6)? d.substring(0, 4)+"...": d;};

.text(croppedText )

Kyle Foreman

unread,
Nov 19, 2011, 9:16:19 AM11/19/11
to d3...@googlegroups.com
I've played around with tspan but I don't believe it will calculate line breaks, will it? It required parsing out lines based on text length, whereas a div makes that easy.

Devang Mundhra

unread,
Nov 22, 2011, 10:49:47 PM11/22/11
to d3...@googlegroups.com
Hi,

I have written a small function to be able to get wordwrap withing an svg rect element properly.

There are 2 functions -
1. fontSize - find the biggest font that can fit atleast one word in one line in that rect
2. wordWrap - using this fontsize, keep breaking the data into new lines and set them in the rect one after the other
    If a word is so big that it can not be set in the rect with the current size, it is appended by '...'

For ease of understanding, the data used is just one depth from flare.json. It has been extended to demonstrate the wrapping (no use wrapping with single words!)

http://bl.ocks.org/1387786

Hope this helps.

Thanks,
Devang

Bob Monteverde

unread,
Nov 30, 2011, 2:22:16 PM11/30/11
to d3-js
Hey Alex,

Sorry I didn't notice this thread was till going after my last post.
I take it you are looking for the SVG example using a clipping path?
(I think there are some posted already, though not finding it right
now).... if you still need it, I'll try to find time tonight, after
work, to post a minimal example.

On the other hand, if you're interested in the generic JavaScript
approach I was initially using (which works quite fine until you have
100's of strings that need to update quickly, and works whether you're
using HTML or CSS) I can post that code (I added a width function and
a shorten function onto the String prototype in JS).

Here's that code (in jQuery... could port to d3 in prob 5 minutes, and
generic JS in a few more):

//Also had a version of this function that took a DOM element or a
string as the font, if its a DOM element it would pull the font
information from it
String.prototype.width || (String.prototype.width = function(font) {
var f = font || '12px arial',
o = $('<div>' + this + '</div>')
.css({'position': 'absolute', 'float': 'left',
'visibility': 'hidden', 'font': f, 'white-space':'nowrap'})
.appendTo($('body')),
w = o.width();

o.remove();

return w;
});

/****
This implementation of shorten is kinda retarded, wasn't concerned
with performance for what I initially wrote this for, BUT I'm sure
with a little thought you could implement something better.
One easy improvement would be to count the number of characters in the
string, and the proportion of the current width vs the needed, and
remove more than a single character at a time...
could likely drastically improve the speed of this.
Another idea I had was to persist the container created by the width
function, so you don't have to generate and delete it on every call.
****/
String.prototype.shorten || (String.prototype.shorten =
function(width, font) {
var f = font || '12px arial',
s = this;

while ( width < s.width() ) {
s = s.substring(0, s.length-1);
}

return s;
});


Again, not really the best performing code... when it comes to SVG and
dealing with > 100 strings, or dealing with a very interactive
visualization that will need to recalculate string width rapidly, a
clipping mask works a lot better.

Bob

Mahir

unread,
Mar 8, 2012, 10:12:42 AM3/8/12
to d3...@googlegroups.com
Hi,

just wanted to share my solution on limiting the text. I use foreignObject element and it seems to be running fine on webkit, haven't tested on firefox or IE9.

      var textField = vis.append("svg:g").selectAll("foreignObject")
.data(myData)
.enter().append("switch").append("svg:foreignObject")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 100) // height needs to be fixed, overflow is clipped.
.style("text-align", "center") // text-anchor is not working but css style is possible
.text(function(d) { return d.textData; });


Hope it will be useful for somebody else,

cheers
M.

Stark

unread,
Apr 9, 2012, 10:35:29 AM4/9/12
to d3-js
Mahir, does this wordwarp the text and choose the largest font size?

Mahir M. Yavuz

unread,
Apr 9, 2012, 5:04:05 PM4/9/12
to d3...@googlegroups.com
Hi,

it basically creates a foreignelement in your svg. so you can assign a class to that and set the font-size of your class in your css file.

M.
Reply all
Reply to author
Forward
0 new messages