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

Fro*_*dik 6 html wysiwyg caret contenteditable

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

首先 - 我有一些文本的可信DIV,我需要得到这个DIV的总行数(行).可能吗 ?

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

有人可以帮忙吗?

Nik*_*las 8

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

要回答第一个问题,如何获取元素中的行数.假设元件使用一致的line-height,你可以通过将要素找到的行数heightline-height.如果您在元素中使用margins,paddings或区分line-heights 元素,这当然会变得更加复杂.如果这不是一个问题,那么该line-height属性不一定必须是一个数值,即它可以是normal一个或一个%等.关于该line-height属性以及如何在这里得到它的数值表示有很多问题.因此,我不会过多地详细说明.为了我的答案的其余部分,我特意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();  
    }

});
Run Code Online (Sandbox Code Playgroud)

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

});
Run Code Online (Sandbox Code Playgroud)