Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I wrap each line of multi-line text in a span?

I've been trying to figure out how to do this (if it's even possible) and have drawn a blank...

I have some text that will wrap onto multiple lines. I want to detect each individual line, and wrap it in a span. Finally, I want to assign a class to each span from a looping array.

For example...!

<div id="quote">
    I have some text that
    wraps onto three lines
    in this container
</div>

I want to get my jQuery to parse those lines, detect where it wraps, and turn it into this:

<div id="quote">
    <span class="red-bg">I have some text that</span>
    <span class="orange-bg">wraps onto three lines</span>
    <span class="yellow-bg">in this container</span>
</div>

The reason that I want to do this dynamically is that I'm doing it within responsive templates, so sometimes the same text will only wrap onto two lines, or maybe four in an iPhone.

Is this doable? I've found this -> http://vidasp.net/tinydemos/numberOfLines.html which calculates the number of lines used in a block of text, but that doesn't really extend to do what I need.

like image 720
BellamyStudio Avatar asked Nov 10 '10 17:11

BellamyStudio


People also ask

How do you text the span of an element?

Use the textContent property to get the text of a span element, e.g. const text = span. textContent . The textContent property will return the text content of the span and its descendants.

What is multiple lines of text?

The Multiline Text Field can be used to store larger amounts of text. The Multiline Text Field offers a lot of formatting options, such as: Adding bulleted and numbered lists. Use bold, italics and underline styling.


2 Answers

It seems like you're asking how to split the text where it is naturally wrapped by the browser. Unfortunately, this isn't straightforward at all. Neither is it robust — consider the following scenario:

  • User browses to your page, the div is rendered and the onload event fires,
  • 3 span elements are created from the text node, 1 for each wrapped line of text,
  • The user resizes the browser and the size of the div changes.

The result is that the spans no longer correlate to where the lines start and finish. Of course, this scenario is avoidable using fixed-width elements or you can rejig the whole thing when the browser resizes, but that's just an example of how it can break.

Still, it's not easy. A similar question has come up before (albeit, with a different goal) and two solutions appeared, which could both be of help here:

Solution 1: getClientRects()

Don't actually wrap the text in spans, but get the position and dimensions of each line of text using getClientRects(). Then, create the number of spans necessary and position/resize them behind each line of text.

Pros

  • Fast; getClientRects returns the position of each line
  • Simple; the code is more elegant than solution 2

Cons

  • Wrapped text must be contained by an inline element.
  • No styling will actually apply to the text (like font-weight or font-color). Only useful for things like background-color or border.

The demo provided with the answer shows how you can highlight the line of text currently beneath the mouse.

Solution 2: Split, join, loop, merge

Split the text into an array using the split() method with a word boundary or white-space as the argument passed. Rejoin the array into a string with </span><span> between each element and wrap the whole thing with <span> and </span>, and replace the original text node with the resulting HTML in the containing element. Now, iterate over each of those span elements checking its y position within the container. When the y position increases, you know that you've reached a new line and the previous elements can be merged into a single span.

Pros

  • Each line can be styled with any CSS property, like font-weight or text-decoration.
  • Each line can have its own event handlers.

Cons

  • Slow and unwieldy due to the numerous DOM and string operations

Conclusion

There may be other ways to achieve your goal, but I'm not sure of any myself. TextNode.splitText(n) can split a TextNode in twain (!) when passed a numeric index of the character you want to split on. Neither of the above solutions are perfect, and both break as soon as the containing element resizes.

like image 126
Andy E Avatar answered Sep 19 '22 08:09

Andy E


I put together a fiddle implementing solution #2 by Andy E (above). I.e. Split, join, loop, merge

Here's the algorithm:

var spanInserted = $('#someText').html().split(" ").join(" </span><span>");
var wrapped = ("<span>").concat(spanInserted, "</span>");
$('#someText').html(wrapped);
var refPos = $('#someText span:first-child').position().top;
var newPos;
$('#someText span').each(function(index) {
    newPos = $(this).position().top   
    if (index == 0){
       return;
    }
    if (newPos == refPos){
        $(this).prepend($(this).prev().text() + " ");
        $(this).prev().remove();
    } 
    refPos = newPos;
});

Enjoy...

like image 24
user2959735 Avatar answered Sep 18 '22 08:09

user2959735