Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The direct answer is that there is no method that actually gets you those numbers. However, there are a number of different work arounds which you can apply to <strong>estimate</strong> (I use <em>estimate</em>, because I don't think they can be made 100% accurate) those values.</p> <p>To answer your first question, how to get the number of lines in the element. Assuming the element uses a consistent <code>line-height</code>, you can find the number of lines by dividing the element <code>height</code> with the <code>line-height</code>. This of course gets significantly more complicated if you got elements with <code>margin</code>s, <code>padding</code>s, or differentiating <code>line-height</code>s within the element. If that isn't enough of a problem, the <code>line-height</code> property doesn't necessarily have to be a numerical value, i.e. it can be <code>normal</code> or a % etc. There's quite a few questions regarding the <code>line-height</code> property and how to get a numerical representation of it here on SO so I won't go too much into detail on that. For the sake of rest of my answer, I did specifically assign a common <code>line-height</code> across the element.</p> <p>The far more problematic part of your question is finding the caret position within the element. Again, there isn't a method which will just return you the right answer. Instead, one approach you can take to <strong>estimate</strong> the caret row position is to make a <code>range</code> at the current caret position (<code>selection</code>), insert a dummy node there, get the nodes <code>offset</code> relative to the containers <code>offset</code>, calculate the number of lines based on the <code>top offset</code> of it, and then remove the dummy element.</p> <p>Additional problems with the dummy element arise when you can't <code>hide()</code> it or leave it empty. </p> <p>It seems like a very bad way of doing it, and it is. It certainly doesn't work flawlessly when trying to navigate with arrows. So why not just use <code>getClientRects()</code> for the <code>selection</code>? It works fine when it is just text without any space or such, but immidiately when you throw a few <code>&lt;br /&gt;&lt;br /&gt;</code> in there, it won't return you the line position there anymore. Additionally, when you are at the first or last character of a line, it would give sometimes incorrect line rows, as it throws the element either up or down depending on the amount of space available.</p> <p>If you are looking for a fool-proof way of finding the caret position and number of lines, there <strong>isn't one</strong>. If rough estimates are enough, than my solution is a good start (it certainly could use some more work, and support for IE which should be lot easier in this case as the <code>TextRange</code> object actually gives offsets in pixels).</p> <p>My take on this:</p> <pre><code>var lineHeight = parseInt($('#editable').css('line-height')); //var ce = $('#editable')[0].getClientRects(); var ce = $('#editable').position(); console.log("Lines: "+$('#editable').height()/lineHeight); $('#editable').bind('click keyup keydown',function(){ if(window.getSelection){ range = window.getSelection().getRangeAt(0); range.insertNode($('&lt;canvas /&gt;').attr('id','tempCaretFinder')[0]); var p = $('canvas#tempCaretFinder').position(); $('canvas#tempCaretFinder').remove(); console.log("Caret line: "+(Math.ceil((p.top-ce.top)/lineHeight)+1)); }else if(document.selection) { // the IE way, which should be relatively easier. as TextRange objects return offsets directly. range = document.selection.createRange(); } }); </code></pre> <p>Example: <a href="http://jsfiddle.net/niklasvh/mKQUH/" rel="noreferrer">http://jsfiddle.net/niklasvh/mKQUH/</a></p> <p><strong>EDIT: Try 2</strong> <a href="http://jsfiddle.net/niklasvh/mKQUH/129/" rel="noreferrer">http://jsfiddle.net/niklasvh/mKQUH/129/</a></p> <p>As mentioned, creating dummy elements made the caret jump all over the place sometimes, so I had a go again with the getClientRects() for ranges. To make them more feasible, I made the selection extend itself one character, then create the range, then check the Rect position, and then move the caret back one character. </p> <p>Why?</p> <p>Because the caret on the first character of a line, would have its top parameter at the same level as the previous line, so with applying an extra character to it, it can check whether it is actually a new line. Another problem with the getClientRects() was that it didn't work on empty line breaks. To accomodate this, I used the previous creating of a dummy element as a fallback in those situations.</p> <p>End result:</p> <pre><code>var lineHeight = parseInt($('#editable').css('line-height')); //var ce = $('#editable')[0].getClientRects(); var ce = $('#editable').offset(); console.log("Lines: "+$('#editable').height()/lineHeight); $('#editable').bind('click keyup keydown',function(e){ //alert($(window).scrollTop()); if(window.getSelection){ var save = window.getSelection(); var s = window.getSelection(); s.modify('extend','forward','character'); // s.modify('extend','forward','line'); range = s.getRangeAt(0); var p = range.getClientRects(); var top; //console.log(p); if (typeof p[1] != "undefined"){ top = p[1].top+$(window).scrollTop(); }else if (typeof p[0] != "undefined"){ top = p[0].top+$(window).scrollTop(); } else{ // sigh... let's make a real work around then range.insertNode($('&lt;canvas /&gt;').attr('id','tempCaretFinder')[0]); var p = $('canvas#tempCaretFinder').offset(); $('canvas#tempCaretFinder').remove(); top = p.top; } // console.log(top-ce.top); console.log("Caret line: "+(Math.ceil((top-ce.top)/lineHeight))); save.modify('move','backward','character'); /* range = s.getRangeAt(0); range.insertNode($('&lt;canvas /&gt;').attr('id','tempCaretFinder')[0]); var p = $('canvas#tempCaretFinder').position(); $('canvas#tempCaretFinder').remove(); console.log("Caret line: "+(Math.ceil((p.top-ce.top)/lineHeight)+1)); console.log(e.which); switch(e.which){ case 40: s.modify("move", "forward","line"); break; case 38: s.modify("move", "backward","lineboundary"); break; } */ }else if(document.selection) { // the IE way, which should be relatively easier. as TextRange objects return offsets directly. range = document.selection.createRange(); } }); </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload