views:

87

answers:

4

I've written a fairly simple script that will take elements (in this case, <p> elements are the main concern) and type their contents out like a typewriter, one by one.

The problem is that as it types, when it reaches the edge of the container mid-word, it reflows the text and jumps to the next line (like word wrap in any text editor).

This is, of course, expected behavior; however, I would like to pre-format the text so that this does not happen.

I figure that inserting <br> before the word that will wrap would be the best solution, but I'm not quite sure what the best way to go about doing that is that supports all font sizes and container widths, while also keeping any HTML tags intact.

I figure something involving a hidden <span> element, adding text to it gradually and checking its width against the container width might be on the right track, but I'm not quite sure how to actually put this together. Any help or suggestions on better methods would be appreciated.


Edit:

I've managed to write something that sort of works using jQuery, although it's very sloppy, and more importantly, sometimes it seems to skip words, and I can't figure out why. #content is the name of the container, and #ruler is the name of the hidden <span>. I'm sure there's a much better way to do this.

function formatText(html) {
    var textArray = html.split(" ");
    var assembledLine = "";
    var finalArray = new Array();
    var lastI = 0;
    var firstLine = true;
    for(i = 0; i <= textArray.length; i++) {
        assembledLine = assembledLine + " " + textArray[i];
        $('#ruler').html(assembledLine);
        var lineWidth = $('#ruler').width();
        if ((lineWidth >= $('#content').width()) || (i == textArray.length)) {                  
            if (firstLine) { var tempArray = textArray.slice(lastI, i); }
            else { var tempArray = textArray.slice(lastI+1, i); }
            var finalLine = tempArray.join(" ");
            finalArray.push(finalLine);
            assembledLine = "";
            if (lineWidth > $('#content').width()) { i = i-1; }
            lastI = i;
            firstLine = false;
        }
    }
    return finalArray.join("<br>");
}
+1  A: 

You could use the pre tag:

Which displays pre-formatted text,
or you could put the content into a div tag, set a fixed width, and script based upon that.

Eric Haley
A: 

A possible approach is to set p display inline (because default display-block will make p to consume all width even if it has just 1 character) and then as you 'type' check the element width.
Set a tolerance in px (25px for example) and once p's width reaches total available width minus width tolerance you insert <br />

I think this should work...

maid450
I tried this, but as far as I can tell, it only works if the line only needs to wrap once. Once you've inserted one `<br>` and you're on the second line, the width is that of the longest line. So it doesn't work if the text is so long that it needs to wrap multiple times. At least, I think so... I could be misunderstanding.
mattjn
Totally true, I didn't thought this...
maid450
A: 

After playing with the code I edited into the question, I managed to get it working decently.

Code:

function formatText(html) {
    var textArray = html.split(" ");
    var assembledLine = "";
    var finalArray = new Array();
    var lastI = 0;
    var firstLine = true;
    for(i = 0; i <= textArray.length; i++) {
        assembledLine = assembledLine + " " + textArray[i];
        $('#ruler').html(assembledLine);
        var lineWidth = $('#ruler').width();
        if ((lineWidth >= $('#content').width()) || (i == textArray.length)) {                  
            if (firstLine) { var tempArray = textArray.slice(lastI, i); }
            else { var tempArray = textArray.slice(lastI+1, i); }
            var finalLine = tempArray.join(" ");
            finalArray.push(finalLine);
            assembledLine = "";
            if (lineWidth >= $('#content').width()) { i = i-1; }
            lastI = i;
            firstLine = false;
        }
    }
    return finalArray.join("<br>");
}

Not perfect, but it'll do. Thanks, everyone.

mattjn
+1  A: 

The best way (IMO) would be to add the whole word, but have the un-"typed" letters invisible. E.g:

H<span style="visibility: hidden;">ello</span>
He<span style="visibility: hidden;">llo</span>
Hel<span style="visibility: hidden;">lo</span>
Hell<span style="visibility: hidden;">o</span>
Hello

To make it easier, give the span a name, and delete it from the DOM each time.

Vincent McNabb
This is a pretty clever solution, but it would require me to completely rewrite everything I've done. Maybe I'll do it someday, though. Thanks for the idea. :)
mattjn