In my last entry, I discussed how one might achieve something similar to the CSS style rule: "word-wrap: break-word;" in Mozilla-based browsers.
The technique I used to accomplish this centered around using a Mozilla binding to automatically insert an invisible unicode hyphen between the characters of a string contained within any element whose class was set to "wordwrap." With this invisible character in place, Mozilla will happily wrap a long string whenever the element's content overflows its container.
There was a catch with my approach though. I wanted to be able to modify, not only any inner text of a "wordwrap" element, but I also needed the logic of my binding to leave in tact, any HTML that may have existed inside the given element.
The logic I ended up using was to insert the unicode character between each and every character in the element's
.innerHTML (including any HTML tags). Obviously though, this would "mangle" the HTML tags thereby making them useless. So, I then added a bit more logic which utilized regular expressions plus a callback function in a
.replace(..) call to "unmangle" the HTML I screwed up in the first place.
This worked, but as alert reader, Rich Birkby, kindly pointed out, there is an easier way. A way that, as it turns out, is quite simple to implement and doesn't require "mangling" any of the HTML tags within the "wordwrap" element.
Read on to learn more.
This superior technique (IMO) that Rich informed me of was the TreeWalker. A TreeWalker is a an object that you can create in Mozilla-based browsers that allows you to navigate the document tree.
document.createTreeWalker(..) function takes 4 parameters which allow you to specify some specifics about exactly how the walker should "do its walking" (if you will). Each parameter is explained in more detail below:
- rootNode: The node in the document that is to serve as the root node for this tree walker.
- whatToShow: An integer constant that specifies one of several built in filters for selecting nodes to be included in the tree. Listed below are the acceptable values for this parameter:
- filterFunction: This parameter can be a reference to a filter function. Its purpose is to allow you to filter nodes even further than what the
whatToShowparameter can provide. If you have no need for this parameter, simply pass in
null. However, if you specify a function, it must accept a single node and return an integer value based on one of the following constants:
- entityRefExpansion: A boolean value that determines whether or not the content of the entity reference nodes should be treated as hierarchial nodes. In most cases, this value will probably be
Updating the original code to use the TreeWalker:
For our purposes (emulating the word-wrap CSS property) we won't need to get very fancy with the TreeWalker at all. All we really need to do is traverse the tree of the element that's been bound by our Mozilla Binding. We'll make use of the SHOW_TEXT filter to ensure that we visit only text nodes (and not bother with any HTML elements). This will allow us to insert the unicode hyphen between the characters of text nodes, while leaving in tact all other HTML inside the bound element.
The code would look something like this:
var node = walker.currentNode;
node.nodeValue = node.nodeValue.split('').join(String.fromCharCode('8203'));
The above code is really a tremendous simplification from what I had originally written (thanks Rich!). This technique allows us to walk only the text nodes of the bound element (skipping over any HTML tags). That way, we can insert the unicode symbol only amongst the characters of raw text and not have to worry about mangling (and subsequently, unmangling) any HTML tags.
While the TreeWalker technique greatly simplifies the word-wrap binding code, there is still a caveat that you should be aware of if you plan to use this binding -- or a similar one you've written yourself -- in one of your projects (and this goes for the original version too).
Inserting the unicode symbol between every character in a string is not necessarily ideal. Take a perfectly normal string like "How are you today?" This string already has spaces, and so it will naturally wrap where the browser sees those "soft" spaces. Inserting the unicode hyphen into that aforementioned string doesn't do anything particularly useful, and in fact, it might even be considered harmful.
It's quite possible that you would end up breaking the word "today" onto two pieces: "tod" and "ay" -- clearly not the desired effect. What can be done about this? Well... one option is careful and thoughtful use of the word-wrap binding. Use it only for long unbroken strings of text (like a long URL for instance). Another option might be to modify the code to abort the word wrap if a space is found within the string (far from a perfect approach though).
Another idea I experimented with (but decided against blogging about because of its complexity) was measuring each "word" in the bound element. If any given word were greater in width than the bound element's container, then that word would get unicode hyphens inserted into it. All "short" words were left alone.
This technique did actually work, but it involved recursively adding invisible, cloned elements wrapped in <span> tags to the DOM on the fly in order to properly measure the text. Also, it did not account for a scenario where text should wrap because of a floated element, and this (IMO) is one of the more desirable way to utilize word-wrap. Still, I think the concept has potential, and I may decide to revisit it at some point in the future.
For now though, we have something that gets us close, is relatively simple to implement, and brain-dead easy to use.
If you'd like, you can see a working demo of the updated word-wrap code.
If you're feeling extra adventurous, you can take a look at my word-wrap experiment (take 3) where I try to measure text elements and only insert unicode hyphens where necessary (use this version at your own risk!)
That'll do it for this entry. Special thanks to Rich Birkby for enlightening me about the TreeWalker. Be sure to check our Rich's ASPAdvice blog: http://aspadvice.com/blogs/rbirkby and his Microsoft Downloads RSS Feed.