views:

2969

answers:

10

I have some dynamic text contained in a div that is set to whatever a user enters in a textbox field. If the text doesn't fit in the div, right now it just gets cut off at the edge and all the text that extends past the border is not visible. I would like to truncate the text so that it fits inside the box and has an ellipsis (...) appended on the end. For example:

|----div width------|
 Here is some sample text that is too long.
 Here is some sam...

Obviously in the example it's easy because the code tag uses a fixed-width font so it's as simple as counting characters. I have a variable-width font so if they enter "WWWWWWWWWWW" it will fill up in much fewer characters than "................" would.

What's the best way to do this? I found a potential solution for simply finding the actual pixel width of the text here: http://www.codingforums.com/archive/index.php/t-100367.html

But even with a method like that, it's a bit awkward to implement the ellipsis. So if it's 20 characters and I find that it doesn't fit, I would have to truncate to 19, add the ellipsis, and then check if it fits again. Then truncate to 18 (plus the ellipsis) and try again. And again. And again...until it fit. Is there a better way?

EDIT: I have decided based on the answers that my original approach is best, except I have tried to improve on the solution in the link above by not creating a separate td element simply for measuring, which feels like a hack.

Here is my code:

<div id="tab" class="tab">
    <div id="tabTitle" class="tabTitle">
        <div class="line1">user-supplied text will be put here</div>
        <div class="line2">more user-supplied text will be put here</div>
    </div>
</div>

And styles:

.tab {
padding: 10px 5px 0px 10px;
margin-right: 1px;
float: left;
width: 136px;
height: 49px;
background-image: url(../images/example.gif);
background-position: left top;
background-repeat: no-repeat;
}    

.tab .tabTitle {
    height: 30px;
width: 122px;
    overflow: hidden;
    text-overflow: ellipsis;
font-size: 12px;
font-weight: bold;
color: #8B8B8B;
}

.tab .tabTitle .line1, .tab .tabTitle .line2 {
    display:inline;
    width:auto;
}

and the javascript that trims and adds the ellipsis:

function addOverflowEllipsis( containerElement, maxWidth )
{
    var contents = containerElement.innerHTML;
    var pixelWidth = containerElement.offsetWidth;
    if(pixelWidth > maxWidth)
    {
        contents = contents + "…"; // ellipsis character, not "..." but "…"
    }
    while(pixelWidth > maxWidth)
    {
        contents = contents.substring(0,(contents.length - 2)) + "…";
        containerElement.innerHTML = contents;
        pixelWidth = containerElement.offsetWidth;
    }
}

The "line1" and "line2" divs get passed to the function. It works in the case of a single word input like "WWWWWWWWWWWWWWWWW" but does not work a multi-word input "WWWWW WWWWW WWWWW" because it just adds line breaks and measures the text as being the width of "WWWWW".

Is there a way I can fix this without resorting to copying the text into a hidden measuring element? Some way to set the style of the line divs so that they don't wrap text?

A: 

Try this:

Link One

Update:

If you use the proprietary {word-wrap: break-word;}, IE will force a line break. Modern browsers could have the default {overflow: visible;}, and they would overflow properly, without expanding the container.

Colour Blend
+1  A: 

Quite simply, no, there's no better way without resorting to hacks.

You could, for example, use a position:absolute span to position your "..." on top of the actual content, with overflow:hidden set on the container, and only hide the extra span if the content fits within the container. That'd let you run the truncation code only once.

Personally, I'd just run the pixel width calculation a few extra times. Setting the text and reading the width is a fast operation, so it shouldn't be noticeable.

jvenema
+3  A: 

If you're using jQuery, there's a great plugin I use.

Alternatively, [this example] (404 NOT FOUND!) seems to work cross browser.

Any questions, hit me up in the comments!

404 link: http://www.hedgerwow.com/360/dhtml/text%5Foverflow/demo2.php

Gausie
I'm not using jQuery, so that can't help me. The example you posted is ok, but somewhat buggy and it puts the ellipsis above where it should and doesn't look very good.I have stuck with my original solution (store the text in an expandable container and measure it's width then iteratively slice off characters until it fits). This works for a single long string, but if the text contains spaces the text just wraps instead. I need the text to be a single line, regardless of how long it is or what it contains.
Brian Schroth
YOu should grab a copy of jQuery. It's probably the easiest way of solving this, and you'll certianly find an application for it elsewhere in your development.
Gausie
As a corporate drone getting approval for jQuery is a monumental task, so unfortunately I'm still looking for non-jQuery alternatives.
Brian Schroth
In that case, you might find it useful to look through the plugin I posted and check if you can apply to logic without using the library
Gausie
A: 

No, it's not simple task. I had to do same thing year ago and found that even Internet Explorer (from version 6), Opera and Safari can do it, but no Firefox. So sorry, only hacks can save you

MBO
+2  A: 

The new version of CSS (CSS3) should include text-overflow:ellipsis, which does this for you. It currently works in IE versions 6 and up, as well as Safari and Chrome. It's not supported by Firefox, so this isn't really a useful answer yet, but it's worth keeping in mind that the real best way will, eventually, be to let CSS handle this.

CSS3 spec draft: http://www.w3.org/TR/2001/WD-css3-text-20010517/#text-overflow-props

Supported browsers: http://www.quirksmode.org/css/contents.html (scroll down to "text-overflow" near the bottom)

JacobM
Of course, I still have to support IE6 so that "eventually" is probably 20 years from now :(
Brian Schroth
Actually (and somewhat surprisingly) IE6 already had the text-overflow:ellipsis feature, so you're fine there.
JacobM
Modified my post to indicate which browsers are currently supported: IE6 and up, Safari, Chrome.
JacobM
A: 

Use text-overflow:ellipsis. Its not IE-only... Most major browser can do this.
But if you can't here is a jQuery plugin for this.

NilColor
A: 

try dojox.html.ellipsis

peller
thanks, I'll give it a shot...unlike jQuery, dojo is a library we're using already, so this might be perfect.
Brian Schroth
+4  A: 

Some way to set the style of the line divs so that they don't wrap text?

There you have the white-space: nowrap for. Yes, this works in ancient browsers as well.

BalusC
Thank you, I think this will work perfectly. For bonus points, diagnose why the other day when I tested this, using the actual ellipsis character (…) worked, but today testing it is showing up as an unknown character question mark box.
Brian Schroth
Your response encoding is wrong. Check headers and meta tags. It must be `UTF-8`.
BalusC
+1  A: 

To answer just one of the points you raise:

So if it's 20 characters and I find that it doesn't fit, I would have to truncate to 19, add the ellipsis, and then check if it fits again. Then truncate to 18 (plus the ellipsis) and try again. And again. And again...until it fit. Is there a better way?

A much faster way of finding the correct length is to cut the string in half, test that to see if it fits. If it does: add a quarter of the original length back on, and if it doesn't lop a quarter off, then test that to see if it fits. Repeat recursively with 1/8th, 1/16th, 1/32th, ... of the original length, until this add/subtract value is less than 1.

It's a seeking algorithm: for strings that are longer than just a few words you can typically cut the number of tests you need to perform in the DOM by a factor of 10.

stephband
+3  A: 

This issue must be working on Firefox too.

HTML

<div class="ellipsis">Here goes to long text and when it is takes more than 200px in "end" of the text appears "..."</div>

CSS

.ellipsis{
width:200px;
text-overflow:ellipsis;
-o-text-overflow:ellipsis;
-moz-binding:url('bindings.xml#ellipsis');//issue for Firefox
}

bindings.xml

<bindings xmlns="http://www.mozilla.org/xbl" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"&gt;
<binding id="none">
    <content><children/></content>
</binding>
<binding id="ellipsis">
    <content>
        <xul:label crop="end"><children/></xul:label>
    </content>
    <implementation>
        <field name="label">document.getAnonymousNodes(this)[0]</field>
        <field name="style">this.label.style</field>
        <property name="display">
            <getter>this.style.display</getter>
            <setter>if(this.style.display!= val)this.style.display=val</setter>
        </property>
        <property name="value">
            <getter>this.label.value</getter>
            <setter>if(this.label.value!=val)this.label.value=val</setter>
        </property>
        <method name="update">
            <body>
                var strings= this.textContent.split(/\s+/g)
                if(!strings[0])strings.shift()
                if(!strings[strings.length-1])strings.pop()
                this.value=strings.join('')
                this.display=strings.length?'':'none'
            </body>
        </method>
        <constructor>this.update()</constructor>
    </implementation>
    <handlers>
        <handler event="DOMSubtreeModified">this.update()</handler>
    </handlers>
</binding>
</bindings>
Binyamin
Does this cause any performance issues? Is there a benchmark of any kind using this hack?
jayarjo
Unfortunately, text with that uses this Firefox binding xml thing is unselectable.
timmfin