generated at
scrapbox-position-2
2021-06-08 22:31:23 deprecated
DocumentOrShadowRoot.elementsFromPoint()を使えば、こんな回りくどい当たり判定をせずともDOMを取得できる
hr
指定した座標にあるscrapbox editor上の諸々の情報を取得する函数群
scrapbox-positionの移行先である/programming-notes/scrapbox-positionでの変更を反映した

内容物
getBorder({x,y})
指定した座標に一番近い文字境界を取得する
cursorや選択範囲の端など、ほぼ文字境界に位置する座標に対して用いる
引数
x : ページの可視領域の左上を原点とした座標系でのx成分
y : ページの可視領域の左上を原点とした座標系でのy成分
返り値
ts
type Return = { left?: CharObject; //境界の左側の文字 right?: CharObject; //境界の右側の文字 line: LineObject; // 文字がいる行 } |{ line: LineObject; message: 'No char found'; } | { message: 'No line found.' | 'No border matched.'; };
行頭や行末など、片側の文字がないときはundefinedが入る
境界が見つからなかったときはすべてundefinedになる
getLine({y})
指定した座標上にある行のDOMを取得する
引数
y : ページの可視領域の左上を原点とした座標系でのy成分
返り値
LineObject | undefined
行がなかったらundefinedになる

dependencies
script.js
import {char as c} from '/api/code/takker/scrapbox-char-accessor/script.js'; import {line as l} from '/api/code/takker/scrapbox-line-accessor/script.js'; const scrapboxDOM = { lines: document.getElementsByClassName('lines')?.[0], };


script.js
export function getBorder({x, y}) { const line = getLine({y}); if (!line) return {message: 'No line found.'}; const chars = [...line.DOM.querySelectorAll('span[class^="c-"]')]; if (chars.length === 0) return {line, message: 'No char found'}; // 文字境界のリストを作る const borders = [...chars.map((char, i, charList) => { return {left: i === 0 ? undefined : charList[i - 1], right: char}; }), {left: chars.pop(), right: undefined}]; //console.log({borders});


境界の判定方法
それぞれの文字の中間を判定領域の端とする
行頭や行末など、どちらかの文字がない場合は、一方の文字の width の値を用いる
script.js
const matchedBorders = borders .flatMap(({left: lChar, right: rChar}) => { const lRect = lChar?.getBoundingClientRect?.() ?? rChar.getBoundingClientRect(); const rRect = rChar?.getBoundingClientRect?.() ?? lChar.getBoundingClientRect(); const left = (lChar ? lRect.left : lRect.left - lRect.width) + lRect.width / 2; const right = (rChar ? rRect.left : rRect.right) + rRect.width / 2; //console.log({left, right}); if (left <= x && x < right) return [{left: c(lChar), right: c(rChar)}]; return []; }); if (matchedBorders.length === 0) return {line, message: 'No border matched.'}; if (matchedBorders.length === 1) return {line, ...matchedBorders[0]};
もし複数の文字が見つかったら、 y から絞り込みをかける
script.js
const lineRect = line.DOM.getBoundingClientRect(); const border = matchedBorders .map(({left, right}, i) => { const lTop = left?.DOM?.getBoundingClientRect?.()?.top ?? lineRect.top; const rTop = right?.DOM?.getBoundingClientRect?.()?.top ?? lineRect.top; const top = i === 0 ? lineRect.top : Math.max(lTop, rTop); return {left, right, top}; }) .find(({top}, i, list) => top <= y && y < (list[i + 1]?.top ?? lineRect.bottom)); if (border) return {line, left: border.left, right: border.right}; return {line, message: 'No border matched.'}; }


script.js
export function getLine({y}) { return l([...scrapboxDOM.lines.children].find(line =>{ const rect = line.getBoundingClientRect(); return rect.top <= y && y < rect.bottom; })); }

#2021-06-08 22:32:57
#2021-02-20 02:36:53