7

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

首先 - 我有带有一些文本的 Contenteditable DIV,我需要获取此 DIV 的总行数(行)。是否可以 ?

其次 - 我需要获得插入符号行位置,如果它在第 1、2、3 等行上......

有人可以帮忙吗?

4

1 回答 1

8

直接的答案是没有任何方法可以真正得到这些数字。但是,您可以应用许多不同的解决方法来估算这些值(我使用estimate,因为我认为它们不能 100% 准确)这些值。

要回答您的第一个问题,如何获取元素中的行数。假设元素使用一致line-height,你可以通过将元素除以 来找到行heightline-heightmargin如果您在元素中使用s、paddings 或区分s 的元素,这当然会变得更加复杂line-height。如果这还不够,那么line-height属性不一定必须是数值,即它可以是normal或 % 等。关于line-height属性以及如何在这里获得它的数字表示有很多问题关于 SO,所以我不会对此进行太多详细介绍。为了回答我的其余部分,我确实专门line-height为元素分配了一个公共点。

您的问题中更有问题的部分是在元素中找到插入符号的位置。同样,没有一种方法可以只为您返回正确的答案。相反,您可以用来估计插入符号行位置的一种方法是range在当前插入符号位置 ( selection) 处创建一个,在那里插入一个虚拟节点,获取offset相对于容器的节点offset,根据它计算行数top offset,然后删除虚拟元素。

当您不能使用虚拟元素hide()或将其留空时,会出现其他问题。

这似乎是一种非常糟糕的做法,而且确实如此。尝试使用箭头导航时,它当然不能完美地工作。那么为什么不只getClientRects()使用selection? 当它只是没有任何空格等的文本时,它可以正常工作,但是当你<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) { 
        // the IE way, which should be relatively easier. as TextRange objects return offsets directly.
        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"){
                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();  
    }

});
于 2011-06-04T13:10:05.930 回答