2016-05-02 11 views
9

シンタックスハイライトを書いています。ハイライターは、テキストを入力して矢印キーでナビゲートするとすぐに強調表示を更新する必要があります。キー押しイベントが発生した後のテキストカーソル位置の取得方法は?

私が直面している問題は、 'keypress'イベントが発生すると、まだテキストカーソルの古い位置がwindow.getSelection()であるということです。

例:例では

function handleKeyEvent(evt) { 
 
    console.log(evt.type, window.getSelection().getRangeAt(0).startOffset); 
 
} 
 

 
var div = document.querySelector("div"); 
 
div.addEventListener("keydown", handleKeyEvent); 
 
div.addEventListener("keypress", handleKeyEvent); 
 
div.addEventListener("input", handleKeyEvent); 
 
div.addEventListener("keyup", handleKeyEvent);
<div contenteditable="true">f<span class="highlight">oo</span></div>

、単語 'foo' で前にキャレットを置き、その後、(右矢印キー)→を押してください。

お気に入りのDevToolのコンソールで次のように表示されます:

keydown 0 
keypress 0 
keyup 1 

0keypress以外は明らかに古いキャレット位置していること。

keydown 0 
keypress 0 
keydown 1 
keypress 1 
keydown 1 
keypress 1 
keydown 2 
keypress 2 
keyup 2 

私は何を取得したいが、私は「からkeyup」または「入力」のそれを得るのと同じように新しいキャレット位置がある:あなたがダウン少し長く→保持する場合は、このような何かを得るでしょう。 「キーアップ」は遅すぎます(実際に何らかの入力があったとしても入力が行われませんが、は入力を生成しません)。

入力時だけでなく、キャレット位置が変更された後に発生するイベントはありますか?または、テキストカーソルの位置を計算する必要がありますか? (私はこれはときにテキストラップはかなり複雑に得ることができ、あなたが(下矢印キー)を押すと仮定します。)

+1

keypressイベントを使用して別の問題があると思います。矢印キーを押すとChromeがイベントを発生させないように見えます(Firefoxはそうです)。ステータスWontFixに古いクロムバグがあります。https://bugs.chromium.org/p/chromium/issues/detail?id=2606 – Alex

+0

ヒントありがとうございます!そうです、 'keypress'イベントは発生しませんが、明らかに廃止とマークされています(https://www.w3.org/TR/uievents/#legacy-keyboardevent-event-types)。私が今見たように、この仕様では、人々は代わりに 'beforeinput'イベントを使うことを勧められていると述べています。また、「キーダウン」も継続して発砲される。これらすべての出来事は早すぎて解雇されます。私は[UI Events spec。](https://github.com/w3c/uievents/issues/111)に問題を提出して、これに対する適切なイベントを得ることを希望しました。 –

+1

カーソルの位置/選択などは、まだすべてのブラウザでサポートするピタです。私はこれを必要としている間はしばらくしていましたが、カーソルに関連するものでは、[ランジ](https://github.com/timdown/rangy)は非常に固いものです。 –

答えて

5

あなたは非同期​​イベントを処理するためにsetTimeoutを使用することができます。

function handleKeyEvent(evt) { 
 
    setTimeout(function() { 
 
     console.log(evt.type, window.getSelection().getRangeAt(0).startOffset); 
 
    }, 0); 
 
} 
 

 
var div = document.querySelector("div"); 
 
div.addEventListener("keydown", handleKeyEvent);
<div contenteditable="true">This is some text</div>

この方法は、キー処理の問題を解決します。

function handleKeyEvent(evt) { 
 
    var caretPos = window.getSelection().getRangeAt(0).startOffset; 
 

 
    if (evt.type === "keydown") { 
 
    switch(evt.key) { 
 
     case "ArrowRight": 
 
     if (caretPos < evt.target.innerText.length - 1) { 
 
      caretPos++; 
 
     } 
 
     break; 
 

 
     case "ArrowLeft": 
 
     if (caretPos > 0) { 
 
      caretPos--; 
 
     } 
 
     break; 
 

 
     case "ArrowUp": 
 
     case "Home": 
 
     caretPos = 0; 
 
     break; 
 

 
     case "ArrowDown": 
 
     case "End": 
 
     caretPos = evt.target.innerText.length; 
 
     break; 
 

 
     default: 
 
     return; 
 
    } 
 
    } 
 
    console.log(caretPos); 
 
} 
 

 
var div = document.querySelector("div"); 
 
div.addEventListener("keydown", handleKeyEvent); 
 
div.addEventListener("input", handleKeyEvent);
<div contenteditable="true">f<span class="highlight">oo</span></div>
:あなたの例では、あなたはまた、

window.getSelection().getRangeAt(0).startOffset 
+1

'setTimeout()'を使用すると、 。だから、ありがとう!次の日に誰もより良い解決策を見つけられなかったら、私はあなたの答えを受け入れるでしょう。私はポジション値の変更を認識しています、私は私の例で単純なコードを提供しました。 'getRangeAt(0)'によって返される範囲は、 'startOffset'が参照する' startContainer'プロパティ内にテキストノードを提供するので、問題ありません。 –

+1

'setTimeout(fn、0)'の呼び出しは、現在の呼び出しスタックが完了した後にコードを実行するためのよく知られているテクニックです。あなたはこれらの記事を見ることができます:[なぜsetTimeout(fn、0)は時々役に立ちますか?](http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful)、[ 0msの遅延がありますか?](https://www.quora.com/What-does-setTimeout-with-a-0ms-delay-do)、[Javascriptチュートリアル](http://javascript.info/tutorial/events - および - タイミング深度)。この[エンターテインメントビデオ](http://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html)を参照してください。 – ConnorsFan

1

によって返された位置の値がここにKeyDownイベント」イベントを使用して位置を補正ソリューションです変更div、内部のspan要素を持っています

残念ながら、この解決策はそのまま、いくつかの欠点があります。

  • 例の<span>のような子要素の内部には、正しいstartOffsetも正しいstartContainerもありません。
  • 複数の行を処理することはできません。
  • すべてのナビゲーションオプションを処理するわけではありません。例えば。 Ctrl + /を介して単語単位でジャンプします。

私が考えなかった問題が多分あります。これらの問題をすべて処理することは可能ですが、実装が非常に複雑になります。したがって、ConnorsFanによって提供される単純なsetTimeout(..., 0)ソリューションは、event for caret position changesが存在するまでは間違いありません。

関連する問題