構文ハイライターを書いています。蛍光ペンは、テキストを入力して矢印キーで移動している間、すぐに強調表示を更新する必要があります。
私が直面している問題は、 '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
それ 0
以外にkeypress
は明らかに古いキャレット位置です。押し続けると → もう少し長くすると、次のようなものが得られます。
keydown 0
keypress 0
keydown 1
keypress 1
keydown 1
keypress 1
keydown 2
keypress 2
keyup 2
取得したいのは、「キーアップ」または「入力」で取得するような新しいキャレット位置です。 'keyup'の起動が遅すぎますが(キーが押されている間、構文を強調表示したい)、 'input'は実際に入力がある場合にのみ起動されます(しかし → 入力を生成しません)。
入力時だけでなく、キャレット位置が変更された後に発生するイベントはありますか?または、テキストカーソルの位置を計算する必要がありますか? (テキストが折り返されて押すと、これはかなり複雑になると思います ↓ (下矢印キー)。
setTimeout
を使用して、keydown
イベントを非同期で処理できます。
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>
この方法は、キー処理の問題に対処します。あなたの例では、span
の内部にdiv
要素もあり、それによって返される位置の値を変更します
window.getSelection().getRangeAt(0).startOffset
「keydown」イベントを使用して位置を修正するソリューションは次のとおりです。
_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>
_
残念ながら、このソリューションにはいくつかの欠陥があります。
<span>
_のような子要素の内部では、正しいstartOffset
も正しいstartContainer
も提供されません。そして、おそらく私が考えていなかったもっと多くの問題があるでしょう。これらすべての問題を処理することは可能ですが、実装は非常に複雑になります。したがって、ConnorsFanが提供する単純なsetTimeout(..., 0)
ソリューションは、 キャレットの位置変更のイベント が見つかるまでは間違いなく推奨されます。