🔚
【JavaScript】選択範囲の終点座標の変更を監視する
目的
input
, textarea
以外の要素において、キャレットの位置に相当する座標を変更されるたびに取得したい。
方法
document.addEventListener('selectionchange', () => {
const selection = getSelection()
const rangeCount = selection.rangeCount
if (rangeCount === 0) {
return
}
// 複数選択を防ぐ – Firefox (Gecko)
if (rangeCount > 1) {
for (let i = 1; i < rangeCount; i++) {
selection.removeRange(selection.getRangeAt(i))
}
}
const caretRange = document.createRange()
const focusNode = selection.focusNode
const focusOffset = selection.focusOffset
caretRange.setStart(focusNode, focusOffset)
caretRange.setEnd(focusNode, focusOffset)
const caretRect = caretRange.getBoundingClientRect()
console.log(caretRect.x, caretRect.y)
})
解説
selection
の focus
は選択範囲の終点を含む node
の情報を返します。
選択方向 (->, <-)
選択範囲 (||)
|------>|
TextNode ( | h | e | l | l | o | )
0 1 2 3 4 5
focusNode: TextNode
focusOffset: 2
|<------|
TextNode ( | h | e | l | l | o | )
0 1 2 3 4 5
focusNode: TextNode
focusOffset: 1
その情報を基にキャレットの範囲となる range
を作ることで終点座標を取得できます。
// range 作成
const caretRange = document.createRange()
// selection の focus を取得
const focusNode = selection.focusNode
const focusOffset = selection.focusOffset
// focus の情報を range にセット
caretRange.setStart(focusNode, focusOffset)
caretRange.setEnd(focusNode, focusOffset)
// range の座標を取得 🎉
const caretRect = caretRange.getBoundingClientRect()
console.log(caretRect.x, caretRect.y)
注意事項
- 取得した座標に常に張り付くような要素を作ると範囲選択に影響が出てしまう。
- 全選択時、
body
の直下の最初か最後にinput
,textarea
が存在する場合に始点か終点の対象要素がbody
になってしまい正しい座標が取得できない。
- 全選択時、
body
の最初か最後にcontenteditable="true"
の要素が存在する場合に位置が最初ならanchor
の値がfocus
に、最後ならfocus
の値がanchor
と同じものになってしまう。
Discussion