Detect Browser Wrapped Lines via JavaScript

Detect browser wrapped lines via javascript

I have to admit at first I thought this would be a daunting task since there is no way to task browser to tell you where auto wrap line breaks take place.

I've created a solution that first wraps each word in a span, then goes through all the spans to determine their top position in container. It then builds an array of indexes of line start and end spans and wraps each line of word spans in a wrapping span.

DEMO: http://jsfiddle.net/KVepp/2/

Possible Limitations:

  • Space added at end of each span may perhaps cause a span to break to
    a new line where text may not.
  • Not sure if each word span needs to be removed once lines are wrapped. ( very simple mod)
  • Assumes no other html in container other than text
  • Needs a little additional work to be turned into a plugin if needed
    for multiple containers
  • regex for words is simple split at space. Likely need additional
    regex for recurring spaces

HTML:

<div id="content">Lorem Ipsum<div> 

CSS:

#content{ position:relative}

JS:

var $cont = $('#content')

var text_arr = $cont.text().split(' ');

for (i = 0; i < text_arr.length; i++) {
text_arr[i] = '<span>' + text_arr[i] + ' </span>';
}

$cont.html(text_arr.join(''));

$wordSpans = $cont.find('span');

var lineArray = [],
lineIndex = 0,
lineStart = true,
lineEnd = false

$wordSpans.each(function(idx) {
var pos = $(this).position();
var top = pos.top;

if (lineStart) {
lineArray[lineIndex] = [idx];
lineStart = false;

} else {
var $next = $(this).next();

if ($next.length) {
if ($next.position().top > top) {
lineArray[lineIndex].push(idx);
lineIndex++;
lineStart = true
}
} else {
lineArray[lineIndex].push(idx);
}
}

});

for (i = 0; i < lineArray.length; i++) {
var start = lineArray[i][0],
end = lineArray[i][1] + 1;

/* no end value pushed to array if only one word last line*/
if (!end) {
$wordSpans.eq(start).wrap('<span class="line_wrap">')
} else {
$wordSpans.slice(start, end).wrapAll('<span class="line_wrap">');
}

}​

Finding line-wraps

Here's what I ended up using (feel free to critique and copy for your own nefarious purposes).

First off, when the edit comes in from the user, it's broken up with $(editableElement).lineText(userInput).

jQuery.fn.lineText = function (userInput) {
var a = userInput.replace(/\n/g, " \n<br/> ").split(" ");
$.each(a, function(i, val) {
if(!val.match(/\n/) && val!="") a[i] = '<span class="word-measure">' + val + '</span>';
});
$(this).html(a.join(" "));
};

The newline replacement happens because the editing textbox is populated with $(editableElement).text(), which ignores <br/> tags, but they will still change the height of the following line in the display for typesetting purposes. This was not part of the initial objective, just fairly low-hanging fruit.

When I need to pull out formatted text, I call $(editableElement).getLines(), where

jQuery.fn.getLines = function (){
var count = $(this).children(".word-measure").length;
var lineAcc = [$(this).children(".word-measure:eq(0)").text()];
var textAcc = [];
for(var i=1; i<count; i++){
var prevY = $(this).children(".word-measure:eq("+(i-1)+")").offset().top;
if($(this).children(".word-measure:eq("+i+")").offset().top==prevY){
lineAcc.push($(this).children(".word-measure:eq("+i+")").text());
} else {
textAcc.push({text: lineAcc.join(" "), top: prevY});
lineAcc = [$(this).children(".word-measure:eq("+i+")").text()];
}
}
textAcc.push({text: lineAcc.join(" "), top: $(this).children(".word-measure:last").offset().top});
return textAcc;
};

The end result is a list of hashes, each one containing the content and vertical offset of a single line of text.

[{"text":"Some dummy set to","top":363},
{"text":"demonstrate...","top":382},
{"text":"The output of this","top":420},
{"text":"wrap-detector.","top":439}]

If I just want unformatted text, $(editableElement).text() still returns

"Some dummy set to demonstrate... The output of this wrap-detector."

How to find whether text in a text area is wrapped to multiple lines?

I experimented on this and came up with a hack that involves the following:

  1. Disabling text-wrapping in the textarea (plus disable padding)

  2. Checking if the textarea's scrollWidth is greater than it's width.

The basic logic i used was that if the text is spanning multiple lines, that means when wrapping is turned off, we should get a horizontal scrollbar.

Demo: http://jsfiddle.net/fnG3d/

Disclaimer: The code below is the result of a 10 minute fiddle, definitely not production quality

function checkMulti(id) {
var ta = $('#'+id), multi = false;

// disable wrapping, padding and enable horiz scoll
ta.addClass('nowrap');

// check if there is anything to be scrolled horizontally (means multi-lines otherwise)
multi = ( ta[0].scrollWidth > ta.width() );

// checking done. remove the class.
ta.removeClass('nowrap');

alert('Spread over multiple-lines: ' + multi);
}

/* the nowrap class */
.nowrap {
overflow-x: scroll;
white-space: nowrap;
padding: 0;
}

JS: Detecting wrapped inline elements?

You can check the offsetHeight property of the elements and watch for it to jump. When an inline element has a greater offsetHeight than the previous element, this element is on a new line.

How to track lines count when words are wrapped?

How about using overflow: hidden?

Say you had a font-size of 12px, and a line-height of 16px.

4 lines * 16px = 64px

.hide-extra-lines {
height: 64px;
overflow: hidden;
}

Is this what you're after?

Textarea - get each line, find line breaks

So the way I did it was:

  • Clone the textarea in question - $("#text") into a transparent textarea. Use transparent font.
  • Change id value of clone to, say, $("#newtext") and append it to DOM.
  • On every keyup, we take the value of $("#text") before this character's keyup. Put that value into $("#newtext") and check if $("#newtext").get(0).scrollHeight() > $("#newtext").height(). If true => this character caused a line break.
  • Increase rows of $("#newtext") in a loop until
    $("#newtext").get(0).scrollHeight() === $("#newtext").height()
  • Take text before this character, add a \n, add this character to $("#newtext").val()
  • Apply $("#newtext").val() to $("#text").val().
  • Remove $("#newtext") from DOM.
  • Repeat all of above steps on every keyup event

Above answer works on similar lines as - stackoverflow.com/questions/3738490/finding-line-wraps – evolutionxbox yesterday

So, basically we convert line breaks into newlines which can be found by using $("#text").val().split("\n").


(Note - If we don't append transparent textarea to the DOM, its scrollHeight() will be undefined)

Detecting line-breaks in browser/css-forced line breaks

Well, assume that your text is in a <p id='wholeText'> :

var element = document.getElementById("wholeText");

var wholeText = element.innerHTML;

var splitText = wholeText.split("\r\n");

var newHtml = null;

for(var i = 0; i < splitText.length; i++)
{
newHtml += "<p class='each-line'>"+splitText[i]+'</p>';
}

Then you can use newHtml to write in a DOM element. For instance, if you like to add it to a the same <p> which you got text from, you do:

element.innerHTML = newHtml; // remember element refers to the <p> with the ID of wholeText

Also put all of the above codes into a function and call the function after the windows has been fully loaded.

window.addEventListener("load", function(){

// put the code here
}, false);

If your line does not contain break characters, then please refer to this plugin :
http://jsfiddle.net/nathan/qkmse/

The above link is suggested in this SO's question:

detecting line-breaks with jQuery?



Related Topics



Leave a reply



Submit