如何获取ContentEditable区域和当前插入符号行位置的行数?

时间:2011-04-03 06:33:37

标签: html wysiwyg contenteditable caret

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

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

其次 - 我需要得到插入行位置,如果它位于第1,2,3行等......

有人可以帮忙吗?

1 个答案:

答案 0 :(得分:8)

直接的答案是没有方法可以实际获得这些数字。但是,有很多不同的工作可以应用于估计(我使用估计,因为我认为它们不能100%准确)值。

要回答第一个问题,如何获取元素中的行数。假设元素使用一致的line-height,您可以通过将元素height除以line-height来找到行数。如果您使用margin s,padding s或在元素中区分line-height s元素,这当然会变得更加复杂。如果这还不够,line-height属性不一定是数值,即它可以是normal或%等。关于{有很多问题{1}}属性以及如何在SO上获取它的数字表示,所以我不会对此有太多细节。为了我的答案的其余部分,我特意在元素中指定了一个共同的line-height

问题中更成问题的部分是在元素中找到插入位置。同样,没有一种方法能够给你正确的答案。相反,您可以采用估计插入符号行位置的一种方法是在当前插入位置(line-height)处生成range,在那里插入虚拟节点,获取节点selection相对于容器offset,根据offset计算行数,然后删除虚拟元素。

当您无法top offset或将其留空时,会出现虚拟元素的其他问题。

这似乎是一种非常糟糕的做法,而且确实如此。当试图用箭头导航时,它肯定无法完美地工作。那么为什么不将hide()用于getClientRects()?当它只是没有任何空间的文本时,它工作得很好,但是当你在那里抛出一些selection时它会立即返回,它将不再返回那里的行位置。此外,当您处于一行的第一个或最后一个字符时,它会产生有时不正确的行行,因为它会根据可用空间量向上或向下抛出元素。

如果您正在寻找一种找到插入符号位置和行数的万无一失的方法,那么不是一个。如果粗略的估计是足够的,那么我的解决方案是一个良好的开端(它当然可以使用更多的工作,并且支持IE,在这种情况下应该更容易,因为<br /><br />对象实际上以像素为单位提供偏移量。)< / p>

我对此的看法:

TextRange

示例: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').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();  
    }

});