The best solution for now :
- Create a temporary div with two spans, and CSS styles made to mimic (font, wordwrapping, scrollbars) the wrapping behavior of the textarea.
- Fill the first span with the text before the caret.
- Fill the second span with the text after the caret.
- Use the offset.top and element height of the spans to know if we're on the first line, or on the last line.
Can I see it working ?
I made a fiddle which makes it obvious how it works : http://jsbin.com/qifezupu/31/edit
How to use it :
I made a jQuery plugin : https://github.com/Canop/taliner.js
Here's the related demonstration page : http://dystroy.org/demos/taliner/demo.html
The code :
$.fn.taliner = function(){
var $t = this,
$d = $('<div>').appendTo('body'),
$s1 = $('<span>').text('a').appendTo($d),
$s2 = $('<span>').appendTo($d),
lh = $s1.height();
$d.css({
width: $t.width(),
height: $t.height(),
font: $t.css('font'),
fontFamily: $t.css('fontFamily'), // for FF/linux
fontSize: $t.css('fontSize'),
whiteSpace : 'pre-wrap',
wordWrap : 'break-word',
overflowY: 'auto',
position: 'fixed',
bottom: 0,
left: 0,
padding: 0,
zIndex: 666
});
var lh = $s1.height(),
input = this[0],
se = input.selectionEnd,
v = input.value,
res = {};
$s1.text(v);
res.linesNumber = $s1.height()/lh|0;
$s1.text(v.slice(0, se));
$s2.text(v.slice(se));
res.caretOnFirstLine = input.selectionEnd===0
|| ($s1.height()<=lh && $s1.offset().top===$s2.offset().top);
res.caretOnLastLine = $s2.height()===lh;
$d.remove();
return res;
}
Does it really work ?
There's still a problem. The only information we get in JavaScript regarding the caret position is element.selectionEnd
. And this is very poor.
Here are the possible caret positions on a two lines textarea :
| A | B | C |
| D | E |
As you can see, there are more possible caret positions than inter-character positions. More specifically you have two different caret positions between the C and the D but the DOM doesn't provide any way to distinguish between them.
This means that sometimes, if you click at the right of the end of the line, the library won't be able to tell if it's the end of the line or the start of the next line.