クリックした位置の node を取得する方法を勘違いしていたのでメモ。
const sel = window.getSelection()
const range = sel.getRangeAt(0)
この window.getSelection()
と getRangeAt(0)
を使うことで選択しているテキストの node 範囲を取得することができる。非常に便利。これをクリックイベントで使うことでクリックした位置の node も取得できるのだけど iOS Safari ではなぜか取得できない現象に衝突。
普通に選択したり textarea
など入力フォームでフォーカスされていれば正常に取得はできるが、なんでもない DOM の場合は取得が下記のようなエラーメッセージでできなかった。
IndexSizeError: The index is not in the allowed range.
iOS Safari でクリックした位置の node を取得するには document.caretRangeFromPoint()
を使う。ただし caretRangeFromPoint
は標準ではないので仕方なく使う感じ。
document.querySelector('.example').addEventListener('click', e => {
// これはだめ
// const sel = window.getSelection()
// const range = sel.getRangeAt(0)
const range = document.caretRangeFromPoint(e.clientX, e.clientY)
})
これで iOS Safari でもクリックした位置の node が取得できる。
ただし、Firefox では caretRangeFromPoint
はサポートしていないので(Chrome はサポートしている)、別の方法を使う。
if (document.caretRangeFromPoint) {
range = document.caretRangeFromPoint(e.clientX, e.clientY)
} else {
range = document.createRange()
range.setStart(e.rangeParent, e.rangeOffset)
// もしくは
// range = document.caretPositionFromPoint(e.clientX, e.clientY)
// もしくは
// const sel = window.getSelection()
// range = sel.getRangeAt(0)
}
Firefox の場合はクリックしたイベントオブジェクトに rangeParent
と rangeOffset
が生えているので、これを元に Range オブジェクトを作成すれば同様の node が取得可能。
また caretPositionFromPoint
を使って(字面が似てるけど Range と Position と違う)取得することも可能。ただし、これは Range オブジェクトじゃなくて CaretPosition オブジェクトが返ってくるので注意。
ただクリックした位置の node と考えると Range オブジェクトである必要はないので、基本は caretPositionFromPoint
で CaretPosition を取得するのを軸にして、対応していないブラウザの場合は caretRangeFromPoint
で逃げるというのが現時点での対応方法かな。