如何获取ContentEditable区域和当前插入符行位置的行数? [英] How to get number of rows in ContentEditable area and current caret line position?

查看:280
本文介绍了如何获取ContentEditable区域和当前插入符行位置的行数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题有两个部分,但它们是相关的。

首先 - 我有Contenteditable DIV的一些文字,我需要得到这个DIV的行数(行数)。是否有可能?



其次 - 如果行号为1,2,3等,我需要得到插入行位置。



任何人都可以帮忙吗?

解决方案

直接的答案是没有任何方法实际上得到你这些数字。但是,您可以应用于估算(我使用估算,因为我认为他们无法做到100%准确)值。



要回答你的第一个问题,如何获得元素中的行数。假设元素使用一致的 line-height ,您可以通过将元素 height 除以行高。当然,如果元素有 margin s, padding s,或者区分<$ c $元素中的c> line-height s。如果这还不足以解决问题,那么 line-height 属性不一定必须是数值,即它可以是 normal 或a%等等。关于 line-height 属性以及如何在此处获得它的数字表示,还有很多问题,所以我在这方面不会有太多细节。为了我答案的其余部分,我专门为整个元素分配了一个共同的 line-height



你问题中有问题的部分要多找到元素中的插入符号位置。再一次,没有一种方法可以让你回答正确的答案。相反,您可以采用一种方法来估计插入符的行位置,即在当前的插入符号位置()设置范围选择),在那里插入一个虚拟节点,相对于容器 offset offset >,根据它的顶部偏移计算行数,然后删除虚拟元素。



当你不能 hide()它或者将它留空时,就会产生其他问题。

这样做似乎是一种非常糟糕的方式,事实确实如此。当试图用箭头导航时,它肯定无法完美地工作。那么,为什么不为选择使用 getClientRects()?当它只是没有任何空格或文本的文本时,它可以正常工作,但是当你在那里抛出几个< br />< br /> 不会再回到你那里的位置了。此外,当您处于某行的第一个或最后一个字符时,它会给出有时不正确的行行,因为它会根据可用空间量向上或向下抛出元素。



如果您正在寻找一种找到插入位置和行数的傻瓜式方法,那么不是。如果粗略的估计足够了,那么比我的解决方案是一个好的开始(它肯定可以使用更多的工作,并且支持IE,在这种情况下应该更容易,因为 TextRange 对象实际上以像素为单位给出偏移量)。



我认为:

  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($('< canvas />').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){
// IE方式,应该相对容易一些,因为TextRange对象直接返回偏移量。
range = document.selection.createRange();
}

});

示例: http://jsfiddle.net/niklasvh/mKQUH/



编辑:试试2
http://jsfiddle.net/niklasvh/mKQUH/129/



如前所述,创建虚拟元素有时会使插入符号遍及整个地方,所以我再次使用getClientRects()来获取范围。为了使它们更可行,我选择了扩展自己的一个字符,然后创建范围,然后检查Rect位置,然后将插入符移回一个字符。



为什么?



由于插入行的第一个字符的插入符号的顶部参数与前一行相同的级别,所以通过对其应用额外字符,它可以检查它是否实际上是新行。 getClientRects()的另一个问题是它不能在空行换行。为了适应这种情况,我在前面创建了一个虚拟元素作为这些情况下的后备。



最终结果:

  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){$ b $ top = p [0] .top + $(window).scrollTop();
}
else {
//叹气...让我们做一个真正的工作,然后
range.insertNode($('< canvas />')。attr( '','tempCaretFinder')[0]);
var p = $('canvas#tempCaretFinder')。offset();
$('canva s#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($('< canvas />').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){
// IE方式,应该比较容易。因为TextRange对象直接返回偏移量。
range = document.selection.createRange();
}

});


my question has two parts, but they are related.

Firstly - I have Contenteditable DIV with some text and I need to get total number of rows (lines) of this DIV. Is it possible ?

Secondly - I need to get caret row position, if it is on row number 1, 2, 3, etc....

Can anybody help ?

解决方案

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 estimate (I use estimate, because I don't think they can be made 100% accurate) those values.

To answer your first question, how to get the number of lines in the element. Assuming the element uses a consistent line-height, you can find the number of lines by dividing the element height with the line-height. This of course gets significantly more complicated if you got elements with margins, paddings, or differentiating line-heights within the element. If that isn't enough of a problem, the line-height property doesn't necessarily have to be a numerical value, i.e. it can be normal or a % etc. There's quite a few questions regarding the line-height 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 line-height across the element.

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 estimate the caret row position is to make a range at the current caret position (selection), insert a dummy node there, get the nodes offset relative to the containers offset, calculate the number of lines based on the top offset of it, and then remove the dummy element.

Additional problems with the dummy element arise when you can't hide() it or leave it empty.

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 getClientRects() for the selection? It works fine when it is just text without any space or such, but immidiately when you throw a few <br /><br /> 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.

If you are looking for a fool-proof way of finding the caret position and number of lines, there isn't one. 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 TextRange object actually gives offsets in pixels).

My take on this:

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($('<canvas />').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();  
    }

});

Example: http://jsfiddle.net/niklasvh/mKQUH/

EDIT: Try 2 http://jsfiddle.net/niklasvh/mKQUH/129/

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.

Why?

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.

End result:

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($('<canvas />').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($('<canvas />').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();  
    }

});

这篇关于如何获取ContentEditable区域和当前插入符行位置的行数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆