scrapboxのcursorの位置を計算する
タイトル通り
用途
先頭から何文字目なのかとか
あとはどの文字が何番目にあるのかも取得したい
実装するmethodは他の機能を実装しながら考える
必要になったものを順次実装する
参考にするcode
今まで作ってきたやつ
一旦文字列に変換してから比較しているが、それを変更して、直接DOMの座標を比較するようにした
文字列→DOM→文字列の二度手間を避ける
Referene
実装
classではなく関数で実装する
classにする意義を感じられなかった
cursor
には .cursor
の縦棒を送る
#text-input
だと選択範囲があるときにうまく認識できない
完成したので別なページに載せる
getCursorInfo.jsexport function getCursorInfo({lines, cursor}) {
// cursorのいる行を取得する
const cursorLine = lines.getElementsByClassName('cursor-line')?.[0];
if (!cursorLine) return {error: 'The cursor doesn\'t has a focus.'};
// 行内の文字を<span>のまま取得する
const chars = [...cursorLine.querySelectorAll('span[class^="c-"]')];
developper toolで確認したところ、 left
の値が同一だった
等号比較で行けるか?
find
で簡単に検索したい
jsconst targetChar = chars.find(char =>
cursor.getBoundingClientRect().left === char.getBoundingClientRect().left);
21:13:31 同一でない文字もあるみたい。
全部確認して、一番近いcharを取得するしかなさそう
21:20:20 cursor.getBoundingClientRect().left
が char.getBoundingClientRect()
の left
と right
の中に収まっているものを探すのならどうだ?
21:24:27 やった!
21:30:31 行先頭の文字番号がおかしい?
0, 0, 1, ...
になる
調べる
どうやら cursor.getBoundingClientRect().left
よりも該当文字の getBoundingClientRect().left
が左にずれている場合があるみたい
どれも小数点以下でずれている
整数値に丸めたらどうなんだろう?
21:46:52 うまくいった
判定は半開区間にした
[..., ...[
閉区間にすると、後ろの <span>
とかぶってしまう
21:58:59 これでもだめなやつがあった
おそらく発生条件
indentあり
先頭が全角文字
半開区間に直すのを忘れてただけだった
22:06:57 最後にcursorが来たら、 \n
にcursorがあると解釈しよう
最後の .c-{number}
に1足したものを返す
getCursorInfo.js // cursorのいる<span>を検索する
const cursorLeft = Math.round(cursor.getBoundingClientRect().left);
const targetChar = chars.find(char => {
const {left, right} = char.getBoundingClientRect();
return Math.round(left) <= cursorLeft && cursorLeft < Math.round(right);});
return {
id: cursorLine.id,
column: targetChar?
parseInt(targetChar.className.replace(/c-(\d+)/,'$1')) :
chars.length, // 改行文字として、最後の文字より一つ後ろの番号を返す
};
}
テストコード
jsfunction getCursorInfo({lines, cursor}) {
// cursorのいる行を取得する
const cursorLine = lines.getElementsByClassName('cursor-line')?.[0];
if (!cursorLine) return {error: 'The cursor doesn\'t has a focus.'};
// 行内の文字を<span>のまま取得する
const chars = [...cursorLine.querySelectorAll('span[class^="c-"]')];
// cursorのいる<span>を検索する
console.log('char pos.: %o', chars
.map(char => char.getBoundingClientRect())
.map(rect => {return {left: Math.round(rect.left), right: Math.round(rect.right)}}));
const cursorLeft = Math.round(cursor.getBoundingClientRect().left);
console.log('cursor left: %o', cursorLeft);
const targetChar = chars.find(char => {
const {left, right} = char.getBoundingClientRect();
return Math.round(left) <= cursorLeft && cursorLeft < Math.round(right);});
return {
id: cursorLine.id,
column: targetChar?
parseInt(targetChar.className.replace(/c-(\d+)/,'$1')) :
chars.length, // 改行文字として、最後の文字より一つ後ろの番号を返す
};
}
addEventListener('keydown',e=>{
console.log(`char info: %o`,getCursorInfo({lines:document.getElementsByClassName('lines')[0],cursor:document.getElementsByClassName('cursor')[0]}))
})
classの名前でいい感じのが見つかったら、別のページに移す
computeCursor.js_disabledexport class cursorLocator {
constructor() {
this._editor = document.getElementById('editor');
this._lines = this._editor.getElementsByClassName('lines')[0]?.children;
}
// cursorがどこかにいるかどうかを返す
hasFocus() {
return !!this._cursorLine;
}
// cursorがいる行の行番号と行idを返す
// タイトル行を0行目とする
// @return {index:number; id:string}?
get line() {
const currentLine = this._cursorLine;
return {index: [...this._lines].indexOf(currentLine), id: this.currentLine.id};
}
cumputeCursor.js_disabled
// cursorのいる行のDOMを取得する
get _cursorLine() {
return this._lines.getElementsByClassName('cursor-line')[0];
}
}
行から文字の座標を取得するのは別のclassに分離するか?
computeChar.js_disabledexport class charLocator {
constructor() {
this._editor = document.getElementById('editor');
}
computeChar.js_disabled // 指定した文字に一致する<span>を探し、はじめに見つかった文字の位置を返す
// @return {index:number; position: {top,left,right,bottom}}?
findChar(char) {}
}