7

私の質問には 2 つの部分がありますが、それらは関連しています。

まず、いくつかのテキストを含む Contenteditable DIV があり、この DIV の行 (行) の総数を取得する必要があります。出来ますか ?

第二に、行番号1、2、3などにある場合、キャレット行の位置を取得する必要があります....

誰でも助けることができますか?

4

1 に答える 1

8

直接的な答えは、これらの数値を実際に取得する方法がないということです。ただし、これらの値を推定するために適用できるさまざまな回避策がいくつかあります( 100% 正確にすることはできないと思うので、私はを使用します)。

最初の質問に答えるには、要素の行数を取得する方法です。要素が一貫した を使用すると仮定すると、要素をでline-height割ることによって行数を見つけることができます。もちろん、要素内に、、または微分を含む要素を取得した場合、これは非常に複雑になります。それが十分な問題でない場合、プロパティは必ずしも数値である必要はありません。つまり、または % などである可能性があります。プロパティとその数値表現を取得する方法については、かなりの数の質問があります。 SO についてはあまり詳しく説明しません。私の答えの残りのために、私は要素全体に共通のものを具体的に割り当てました。heightline-heightmarginpaddingline-heightline-heightnormalline-heightline-height

あなたの質問のはるかに問題のある部分は、要素内のキャレットの位置を見つけることです。繰り返しますが、正しい答えを返すだけのメソッドはありません。代わりに、キャレット行の位置を推定するために使用できる 1 つの方法はrange、現在のキャレット位置に a を作成し ( selection)、そこにダミー ノードを挿入offsetし、コンテナーに関連するノードを取得し、それにoffset基づいて行数を計算することです。top offset次に、ダミー要素を削除します。

hide()ダミー要素を使用できない場合、または空のままにしておくと、追加の問題が発生します。

それは非常に悪い方法のように思えますが、実際にそうです。矢印でナビゲートしようとすると、完全に機能するわけではありません。getClientRects()では、単に使用しないのはなぜselectionですか?スペースなどのない単なるテキストの場合は正常に機能しますが、すぐ<br /><br />にそこにいくつかをスローすると、そこに行の位置が返されなくなります。さらに、行の最初または最後の文字にいる場合、使用可能なスペースの量に応じて要素が上または下にスローされるため、不正確な行の行が表示されることがあります。

キャレットの位置と行数を確実に見つける方法を探しているなら、それはありません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() をもう一度使用しました。それらをより実現可能にするために、選択範囲を 1 文字拡張し、範囲を作成し、Rect の位置を確認してから、キャレットを 1 文字戻します。

なんで?

行の最初の文字のキャレットは、その最上位パラメーターが前の行と同じレベルになるため、余分な文字を適用することで、実際に改行かどうかを確認できます。getClientRects() のもう 1 つの問題は、空の改行では機能しないことでした。これに対応するために、そのような状況でのフォールバックとして、ダミー要素の以前の作成を使用しました。

最終結果:

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 に答える