Emulating CSS word-wrap for Mozilla/Firefox

Be sure to check out the follow-up to this post: Word-wrap for Mozilla (take 2). It features a much simplified way of accomplishing the same thing, based on the comments from RichB.

IE has one nice CSS feature that a lot of other browsers lack, which is the ability to use CSS to wrap long words. It's easy to use. Something like this will do the trick:

.wrapme
{
   word-wrap: break-word;
}

Firefox lacks this feature, but it does offer some snazzy features of its own. One in particular is XBL.

In this blog entry, I'm going to demonstrate how to use Mozilla Bindings to (sort of) emulate CSS word-wrap. Since the ability to wrap a long series of unbroken characters is part of the CSS3 specification, I do expect to see this in future versions of Firefox. Until then, this is the best I've got.

An outside-the-box solution

For a long time, I didn't think there was really anything you could do about Firefox's lack of support for word-wrap. I figured the best you could do was set your containing element to hide overflowing data (which might be okay, but most likely not).

Just recently though, a co-worker of mine found a web page that explained how you could insert an invisible unicode character (​) at various points in a string to solve the Firefox word-wrap problem. Since Firefox views this unicode symbol as a valid character upon which to "break" a string (if necessary) it servers as a convenient to way get Firefox to wrap long strings of characters (the symbol remains completely invisible to the end user).

My co-worker first showed me a function he wrote which took a string parameter, inserted this unicode symbol every 5 characters, and then returned the modified string.

This is a good first start, but I wanted something more. I wanted a solution that didn't require me to execute a Javascript function, and I wanted a solution that could handle HTML intersperced throughout the string.

It turns out, that creating a solution that doesn't require executing a Javascript function isn't the hard part at all! In Firefox, this can be accomplished quite easily with XBL.

How does one utilize XBL? Glad you asked!

From the Mozilla Developer page:

Extensible Binding Language is a XML-based markup language to implement reusable components (bindings) that can be bound to elements in other documents. The element with a binding specified, called the bound element, acquires the new behavior specified by the binding. Bindings can be bound to elements using Cascading Style Sheets (CSS) or DOM. One element can be be bound to several bindings at once.

Setting up the binding is pretty easy, so let's tackle that part first:

.wordwrap
{
   -moz-binding: url('./wordwrap.xml#wordwrap');
}

With the CSS in place, "binding" to the DOM element is trivial: simply set the element's class property to "wordwrap".

<p id = "myParagraph" class = "wordwrap">
   SomeParagraphWithAVeryLongStringOfUnbrokenTextThatWillEventuallyWrap.
</p>

With the CSS and HTML taken care of, all that's left to do is write the XBL file. An XBL file is really nothing more than an XML with a specific structure so that Mozilla knows how to process it as a binding. In fact, if you prefer, you can name the file with an .xml extension (like I did), instead of an .xbl extension.

At its most basic, an XBL file takes the following form:

<?xml version = "1.0"?>

<bindings xmlns = "http://www.mozilla.org/xbl" xmlns:html = "http://www.w3.org/1999/xhtml">

<binding id = "wordwrap" applyauthorstyles = "false">

   <implementation>
      <constructor>
         //
            /* ECMAScript code here */
         //
      </constructor>
   </implementation>

</binding>

</bindings>

The binding id attribute:

The root element of the file is <bindings>. Within the root element, you'll have one or more <binding> elements, each with its own unique id. Since an XBL file can have more than one binding definition, the id instructs the browser as to which binding we're interested in. Remember, back when we specified the url for the -moz-binding property, we specified the binding id by appending to it, a "#" symbol followed by the binding id (in this case "wordwrap"). So the full url ended up being: "./wordwrap.xml#wordwrap".

The applyauthorstyles attribute:

The "applyauthorstyles" attribute determines whether or not to allow styles from the document on the bound element (other than the -moz-binding). The default value is false, but you're free to change this if you want (or need) to.

The <implementation> and <constructor>

Every binding has an <implementation> element and, within it, a <constructor> element. This is where most of your ECMAScript code will go (in our case, this is where all of the ECMAScript code will go).

With a very basic explanation of an XBL file's structure out of the way, we can now go ahead and write the implementation of our binding.

It's important to note that all of the ECMAScript you write needs to go inside a CDATA block (this an XML file after all). Other than that though, you don't have too many restrictions. In fact, one nice thing about writing a binding, is that you have a great deal of flexibility. Pretty much all of the ECMAScript you're used to writing is going to work inside the XBL file. And to sweeten the deal even more, you've got some nice extras that aren't available in your normal, day-to-day ECMAScript (we'll look at one of them shortly).

Referencing the bound element:

Perhaps the most important thing you need to know (before you can do anything productive in your binding) is how access the bound element. In other words, how do you access the element to which the class (in our case "wordwrap") was applied? Fortunately, it turns out to be brain-dead easy. You simply using the this keyword; that's it!

Once you have a reference to the bound element, you can do most (if not all) of the things you'd normally do with a DOM element. You could, for instance, wire up logic such that a particular function execute whenever the bound element is moused over, as in:

var elem = this;
elem.addEventListener('mouseover', function(e)
{
   alert('The text of this element will eventually be wrapped!');
}, false);

If you put the preceding code inside the <constructor> element of the binding definition, then any time a user moused over the <p> (#myParagraph), he would see an alert saying "The text of this element will eventually be wrapped!"

Making the text wrap:

At this point, it should be fairly clear what the plan of action is:

  1. Access the bound element's text.
  2. Insert the special unicode symbol between the characters of the string.
  3. Update the bound element's text with the newly modified text.

From the instructions above, I've made it sounds pretty simple. Unfortunately, it's a bit more complicated than I let on. I (conveniently) failed to mention that if the bound element's text contains HTML, we need to take extra care not to insert the special unicode symbol into any of the HTML tags (or else we'll end up disrupting the author's markup).

Actually, the final solution I came up with doesn't take care not to insert the special unicode symbol into HTML tags. Instead, I willingly make a mess out of the markup and then write a bit of logic to clean it up later on, but I'll explain all of that as we get further into the binding's ECMAScript code.

The word wrap code

The code to make this happen isn't long, so rather than try to explain it up front, I'll first show the full code, and then provide an explanation afterwards:

<?xml version = "1.0"?>

<bindings xmlns = "http://www.mozilla.org/xbl" xmlns:html = "http://www.w3.org/1999/xhtml">

<binding id = "wordwrap" applyauthorstyles = "false">

   <implementation>
      <constructor>
         //

         var elem = this;           // maintain a reference to the bound element

         elem.addEventListener('overflow',
         function()
         {
            // matches any "mangled" HTML tag
            var exp = /<&#8203;\/*[&#8203;_\s="'\w]+>/g;

            var txt = elem.innerHTML;     // the bound element's innerHTML
            var chars = txt.split('');
            var newTxt = chars.join('&#8203;');
            newTxt = newTxt.replace(exp, reconstructTag);
            elem.innerHTML = newTxt;
         },false);

         function reconstructTag(_tag)
         {
            return _tag.replace(/&#8203;/g, '');
         }

         //
      </constructor>
   </implementation>

</binding>

</bindings>

Explanation:

The first thing we do is stuff the bound element (this) in a variable, elem. This is of course, totally optional, but it helps me (my brain is small and remembering what this represents eludes me quickly and frequently).

Next, we add an event listener to the bound element that "listens" for the 'overflow' event (whenever text overflows its containing element). You're probably aware the in typical Javascript, there is no such 'overflow' event. This, along with its counterpart, 'underflow' are just two the many "extras" that you get when writing Javascript in XBL.

The remainder of the code will all take place within the context of the anonymous function that executes on 'overflow'.

Next, we create a regular expression that matches any "mangled" HTML tag (more on this in a bit).

The next three steps are all quite straight-forward. We store the bound element's innerHTML in a variable called, txt. We then call .split('') on that text and store it in the variable, chars. Passing an empty string into the .split(..) function will yield an array of characters representing the string that was split. In other words, each character in the string is now an element in the array chars. Finally, we put all of those characters back together by calling chars.join('&#8203'); and stuffing the result in newTxt. This inserts that special unicode symbol between each character in the array as it joins them back together.

The tricky part:

If you were paying attention, then at this point, you'd realize that any HTML that existed inside of the bound element, just got totally mangled with that last .join(..). statement. Think about it: if the bound element's innerHTML were something like this:

This <span>is a</span> paragraph.

Then after executing the .join(..) statement, the variable, newTxt would now be equal to:

&amp;#8203;T&amp;#8203;h&amp;#8203;i&amp;#8203;s&amp;#8203; &amp;#8203;<&amp;#8203;s&amp;#8203;p&amp;#8203;a&amp;#8203;n&amp;#8203;>&amp;#8203;i&amp;#8203;s&amp;#8203; &amp;#8203;a&amp;#8203;<&amp;#8203;/&amp;#8203;s&amp;#8203;p&amp;#8203;a&amp;#8203;n&amp;#8203;>&amp;#8203; &amp;#8203;p&amp;#8203;a&amp;#8203;r&amp;#8203;a&amp;#8203;g&amp;#8203;r&amp;#8203;a&amp;#8203;p&amp;#8203;h&amp;#8203;.&amp;#8203;

Besides perhaps giving you a head-ache, the above HTML snippet should make you realize that the insertion of that special unicode symbol between every character in the string has a really detrimental effect on the <span> tag.

The solution:

The follow-up to this post explains a much easier method that doesn't involve mangling the HTML. It is highly recommended that you check out that approach as well.

It turns out, that "unmangling" the HTML tags isn't too too hard (just takes some fancy regular expression work). The regular expression, exp will match any HTML tag that has had the special unicode symbol inserted into it. We can utilize this regular expression to match all occurrences of "mangled" HTML tags and (with some help from the .replace(..) function) replace them with "unmangled" versions of the tag.

In order to do this though, we need to make of use a callback function when we invoke the .replace(..) function. When you pass a function reference as the second parameter into a .replace(..) call (as in: myString.replace(regex, myFn)) Javascript will automatically pass the matched string into the callback function for you!

This is an extremely powerful concept. We can now take all "mangled" HTML tag matches and send them through the reconstructTag(..) function. The reconstructTag(..) function simply returns a copy of the string sent into it, but with all occurrences of that special unicode symbol stripped out (thus returning our "mangled" HTML tags to proper form).

With all of these concepts working together, we end up with a string that looks perfectly unaltered to the end user, but which wraps nicely when it would otherwise overflow its container.

See for yourself on my demo page: Word Wrap Binding Demo.

And, to see a slightly more complex example, take a look at my implementation of an ellipsis for Mozilla using XBL: Mozilla Ellipsis.

Final thoughts:

Mozilla Bindings offer tremendous power to the developer. You can do virtually anything with them; the sky is the limit really. In this blog post, we just looked at one example: how to make long, unbroken strings wrap when they would otherwise overflow their container. But we also saw how you can take this further (see the ellipsis demo link above).

Important Warning / Disclaimer!

It should be noted that I have absolutely no idea what kind of effect inserting the special unicode symbol (needed to make this technique work) might have on accessibility. It's an important question. Is that character going to be read aloud by screen readers (what in the world would it say)? Will search engines see it and have no idea what your text represents, thereby hurting your search engine ranking? My fear is that the effect of inserting this character into your string might not be good. But of course, without any real testing, I can't say for sure.

My recommendation would be to use this concept with caution and only when absolutely necessary. A little common sense will go along way towards ensuring you're not bloating your page will a lot of "fancy-just-for-the-heck-of-it" code.

So there ya go! Word wrap in Mozilla. Happy Binding!

37 Responses

  1. RichB Says:

    Another cross-platform way I use from time-to-time is:
    $("element").innerHTML=$("element").innerHTML.split("").join("");

  2. RichB Says:

    It should be possible to do this correctly the first time using a treewalker. eg:

    var walker=doc.createTreeWalker(elem, NodeFilter.SHOW_TEXT, null, false);

    while(walker.nextNode()) {
    walker.currentNode.nodeValue=walker.currentNode.nodeValue.split("").join("\u8203");
    }

  3. RichB Says:

    My first comment had the HTML stripped from it….
    It should read:

    .split("").join("[wbr]");

    (where the [ and ] are replaced with angle brackets to create the wbr tag.)

  4. sstchur Says:

    RichB,

    Thanks! These are excellent suggestions. I had completely forgotten about the <wbr> tag. That's a good idea; I'll verify it works and make the change if so.

  5. Nico Westerdale Says:

    And how exactly is this better than actually implementing word-wrap: break-word;? Why don't the FF developers just bite the bullet and make it work like the CSS Level 3 standards compliant way that IE does it.

  6. sstchur Says:

    Nico,

    It's not better. IE's implementation is better, and I imagine that Firefox most likely will support it in the future.

    To be clear though, this is not a case where IE "implemented the standard." This is a case where IE implemented a proprietary feature (and a good one) which eventually became a standard. So it's natural that it would take a little bit of time for other browsers to catch up and implement that standard.

  7. Darleen Says:

    You must be crazy!! All that to get FF to word-wrap …
    Must be an easier method, then mangling the HTML , etc etc
    Can we just wrap the text??

    Sheesh

    ..darleen

  8. sstchur Says:

    Darleen:

    I admit, it is a lot, but what else can we do? If you read the follow-up post, it has a simplified method that you might prefer: http://ecmascript.stchur.com/2007/03/01/word-wrap-for-mozilla-take-2/

  9. hatemozilla Says:

    Cant believe i have to do so much just to wrap a word…Mozilla guys cant get any more foolish…First of all they have ignored most important feature,on top of it some "firfox advocate" is giving some stupid solutions…I am a novice software engineer who has been assigned a bug related to wrap…there is no way i can implement this solution…where do i place this XML??do i need to parse this…how do i get my CSS access this??

  10. David Says:

    I agree. It's just foolish to have to go through all this just to wordwrap a continuous line. I love Firefox, and will continue to use it as my primary browser, and will continue to write web sites for this browser, but I am disappointed that Mozilla can't see to it to implement useful, and sometimes necessary implementations such as this wordwrap issue and another that's been plaguing me; fully featured scroll bars for iframes. IE has made both of these issues very available and very easy to implement with CSS. But we can't ignore that Firefox is increasingly popular, and the developers need to understand that the robust nature of these features makes them worthy for immediate implementation. I'm disappointed that Mozilla is not keeping up.
    David

    PS – Oh, and by the way, Mr.sstchur, this very textarea form field doesn't seem to do what your formula suggests. There truly must be a better way.
    ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd

  11. sstchur Says:

    You people don't seem to understand that I'm trying to /help/ you here. I'm not saying this simulation is as good as native word-wrap support. Firefox is an extraordinarily powerful browser, and the binding mechanism discussed in this post demonstrates that.

    My particular implementation of word-wrap has its flaws and I point that out. But my inability to write the "perfect" word-wrap implementation for Firefox doesn't make it any less great of a browser.

    Furthermore, I suspect that future versions of Firfox will indeed support word-wrap. Until then, what's we've got here is at least /something/.

    And finally, David, I'm not using the word-wrap solution on this blog. Truth be told, I run into very few cases where word-wrap was really worth it for me. It doesn't bother me that your "dddddd" example is spilling out over the boundaries. I decided to accept that when I created this blog's design. You're more than welcome to dislike it.

  12. Holf Says:

    All I can say is thanks so much for all your efforts to sort this out. To everyone else: no, he is not crazy! There really is no simple answer to the Word-Wrap problem in Firefox. stchur's solution, and the two follow-up solutions, really are the closest I've seen to a true resolution of this problem.
    And even if it does seem complex, stchur's done all the hard work anyway :-)

    And solution 3 (http://ecmascript.stchur.com/blogcode/emulating_word_wrap_take3/) is truly awesome…

  13. xxcemil Says:

    about css parameters
    http://css-lessons.ucoz.com/css-parameters.htm

  14. html-dersi Says:

    HI i need your help i really want to create my own website/web page but i dont know how to go about doing it so can you please help me out

  15. dblurred Says:

    I was able to get this to work, but the screen is blank even though the html is there. I pop the source code into Dreamweaver and it is the same as if the wordwrap css was not there. Same exact code/html, but one shows up and the other not. Is it possible that I am missing something? I used your exact example code above.

    Thanks.

  16. sstchur Says:

    dblurred:

    You can try emailing me a zip of your sample if you want. I'm not sure what the issue is, but I could try to take a look.

    -Steve

  17. William from Lagos Says:

    Hey thanks. Your post just saved me a great deal.

  18. mLi Says:

    can i use this in table cell ….??

  19. sstchur Says:

    @mLi:

    You know, I'm not sure. I use tables so rarely these days, that I've never tried. Give it a shot and let me know how it works.

  20. venkat Says:

    its not working , when i use mozilla still it has same problem event , when i copy the code ie:xml file it show so many error.

  21. sstchur Says:

    venkat:

    Can you be a bit more specific? What, exactly, isn't working?

    Does the live demo work for you? http://blog.stchur.com/blogcode/emulating_word_wrap_take2/

  22. destiniy Says:

    thanks a lot, your take 2 ver worked just fine

    again thanks a lot

  23. Pandamouse Says:

    Thanks heaps for that helped a great deal to make my own wrap solution for firefox.

  24. jeff Says:

    very cool. i'm the new web guy on a LARGE cms environment and the ff/ww issue has been a big pain. i shall now use this as a bit of php code that scans all posts for words larger than x chars and only run on those words that need it. mostly links i suspect. (yes, i tested it on an and it works).

    and for those people that are mad that this fix isn't easier, please keep two things in mind;

    1) no one ever said g o o d programming and web building was easy. (not to mention, the guy's done it all for you in this post…)

    and

    2) firefox is free. tons of talented programmers have donated a lot of time on ff for no other reason than to provide a better alternative to the ridiculously horrendous ie. perfect it aint but it's darn close. as a programmer myself, i don't think i could live without it.

    thnx again sstchur !

  25. sstchur Says:

    @jeff:

    Thanks for your comments. Glad you found it helpful!

  26. Word Wrap Preformatted Text in Mozilla Firefox « За Виртуалния Салам…и…още нещо! Says:

    [...] Blog Post About Word Wrapping Unbreakable Strings [...]

  27. Incoherent Babble " Blog Archive " Emulating CSS word-wrap for Mozilla … | CSS Tutorials - CSSHelper.net Says:

    [...] Source [...]

  28. Chatterly Says:

    This has been very helpful for me. It is a shame we have to add so much for such a simple problem. My sites go through a CMS and even though I don't readily use tables, the content editors LOVE them for their data. So my choices are to let them screw up the site or use a heavier solution like this one. Thanks for sharing this one.

  29. IE,FIREFOX文本换行问题 « hellosap Says:

    [...] 在div+css的设计当中,文本的自动换行是一个比较难以处理的问题。IE下的方法很好解决。用css word-wrap 和break-word就可以解决了,firefox下的文本不支持单词间的自动段行,有两种方法可以解决这个问题,一种是采用javascript重写文本,还有一种是用一种不常见的方法,用一个xml来解决这个问题 具体参考如下链接,这是一个浏览器相关的解决方案,本质上其实也是用javascript来解决,不过事件是在ove 引用文献 [...]

  30. 如何跨浏览器使用连续字符的换行 -Cacique9.com Says:

    [...] Firefox ,你还可以通过 XBL bindings 来实现此效果:《Emulating CSS word-wrap for Mozilla/Firefox》 标签: 发表评论 | Trackback [...]

  31. Aleks Says:

    Need to set "table-layout: fixed" for word-wrap to work

  32. xiaohao Says:

    i like this way, but i don't know much about xml,maybe i need to learn it these days

  33. roger Says:

    firefox now supports word-wrap :D

  34. sstchur Says:

    Woo hoo! It's about time!

  35. Jitu Says:

    Hi…appreciate your work for word-wrap solution in mozila.

    I had implemented your solution in my work but it hides that data in mozila … my code as below :

    29072010120834_1280385514698_XXXPN6200X_ITRV.pdf

    I had also put the wordwrap.xml at root also.

    Can you please provide the solution ?

    Thanks for Help

  36. 如何跨浏览器使用连续字符的换行 | HTML5 CSS3 JavaScript Says:

    [...] 对于 Firefox ,你还可以通过 XBL bindings 来实现此效果:《Emulating CSS word-wrap for Mozilla/Firefox》 [...]

  37. Cute Nightgazer Starlight Says:

    Huh?

    I don't understand?

    Where do I put this?

Got something to say?

Please note: Constructive criticism is welcome. Rude or vulgar comments however, are not and will be removed during moderation.