【HTML/JavaScript】位置情報扱い方法まとめ【scroll/mouse/element/document/window】


JavaScriptなどで位置に関する処理を行うときに、
多くのサンプルプログラムで勘違いや間違い、
わかりにくいところが多いので大分混乱しました。
(限定的なケースでのみうまくいっているように見える。
でもなにかの時に動かないとか)

やっと本質的なことがわかってきて理解してきたので
時間節約のため、今覚えているうちに、
ポイントをまとめておきたいと思います。
(MDNのFirefoxベース)

うっかりしていた、間違っていた、間違いやすそう、あいまいに理解して問題が起こりそうなところには😰を置きました。

おもに3つの座標系がある

まず初めに自分が何の値を使っているのか、
話しているのかを明確にするために
次の基準点を理解しないといけない。

表す座標系呼び方
Webページ内での位置
(document文書の左上隅を基準)
ローカル座標⇒ドキュメント座標系とここでは呼ぶことにする。
ブラウザ、タブウインドウ内での位置
(ウインドウ左上を基準)
ビューポート
デスクトップでの位置グローバル座標
スクリーン座標
スクリーン
デスクトップ
ブラウザ
document
---

 ...
i
ユーザ

ドキュメント座標とは HTML文書(document)内での相対位置で、BODYの一番上が 0,0になる。
ビューポートとは、見ている範囲(view/window)。つまりブラウザで現在表示されている範囲(窓)の左上が 0.0になる。
同様にスクリーン座標では、ユーザが見ているディスプレイスクリーンの左上0,0からみた相対位置になる。

オブジェクト使用時の関連付けの覚え方:マウスなどのイベントはよく考えれば、ブラウザ内の位置であることは納得がいくし、エレメントでの位置やスクロール位置はページドキュメント内の位置になる。ビューポートは見た目視点なので、windowだと理解しやすい。ローカルという名称だけはイメージがしにくい。
mousedown - Event reference | MDN

これがわかると、実は
「絶対座標」 は ローカル? グローバル?かあいまいな表現だとわかる。

変数に位置を覚えさせるときのスコープが広い場合 、
どの座標系かを変数名の命名規則につけておくと間違いにくいだろうと思う。

補足

座標を考えるうえで、知っておきたい概念を説明する。

(飛ばして後から確認してもいい内容となっている)

 padding/content edge

座標系を考えるうえで、要素の左上が実際どの点を指すのか正確に知っておく必要がある。考えるポイントとしては、HTMLのレイアウトにおけるボックスの位置関係が大事。

  • padding edge:  border と padding の境目
  • content edge : padding と content の境目  (content は テキストなどが描かれる位置)

その前の、 boarder edge , margin edge も同様に外側との境界
CSS ボックスモデルの紹介 - CSS | MDN

実は左上は 上記リンク先の図のcontentのところ。予想以上に内側にある。
box-sizingで設定せずに 幅100%指定しているのは実はイメージが間違っている。😰

CSSでいう 100vw はビューポート座標で、100% は content edgeのサイズになる。
bodyが100%であれば、スクロールバーに影響されずに100%の幅でブラウザは表示させるから element.clientWidthが スクロールバーの幅を除いたウインドウ幅になる。

スクロールバー

全てのスクロールバーは クライアント内に存在

border > padding(scrollも) > テキストコンテンツ

にあり、実は、右端のスクロールバーも同じ考えで、ウインドウに付属しているものではなく、各要素の一つであるdocumentのclient内に要素に表示しきれていない範囲があるから、要素内で出現させているという設計思想だった。😰

※HTMLでいう  clientはblockなど各要素の内側の範囲を指す。参考図:MDN

document.documentElement

HTML文書では htmlです。document.bodyよりも上位ということ。
document.documentElement - Web API インターフェイス | MDN

単位

基本的には Integer で px単位を指定・取得する。もちろん styleなどは "10px"のように指定する。

位置情報関連の取得/設定とオブジェクトの関係

さっそく、スクロールや位置関連の関数を見ていこう。

この章では
まずは、座標系毎の関数やプロパティをまとめ、
後半はオブジェクト(window, event ...)ごとにどの座標系を使っているかを表示する。  さらに補足として、サイズ関連の情報もまとめて追加しておいた。

 座標系の取得

座標系毎の関数やプロパティをまとめている。

デスクトップ基準のスクリーン座標

まずはスクリーン座標から。

使用方法意味
window.screenXウインドウの位置
window.screenX - Web API インターフェイス | MDN
event.screenXマウスなどイベント時の位置
Event reference | MDN

  ちなみに、

  • window.mozInnerScreenX  は スクリーン座標系で、 window内の左上のピクセル位置。

ウインドウのビューポート座標

使用方法意味
event.clientXイベントの発生場所
element.getBoundingClientRect()

ビューポート
element.getBoundingClientRect - Web API インターフェイス | MDN

補足:
window.innerWidthは ビューポート内の幅   100vwに近いが下に記載したように デフォはcontent edgeのサイズ

ドキュメント内のローカル座標

使用方法意味
window.scrollX
window.page XOffset
ウインドウのスクロール位置(document左上からwindow左上)
(pageXOffsetは MDNではエイリアス)
window.scroll()ウインドウのスクロール位置を設定

オブジェクトごと

上記と内容は重なる部分はあるが、オブジェクトを軸に整理した。

オブジェクトごとにどの座標系となるかのみ記している。

オブジェクトプロパティ座標系
windowscreenXスクリーン
scrollX    / scroll() /scrollTo()ローカル/ドキュメント
eventclientXビューポート
screenXスクリーン
pageXローカル/ドキュメント
(正式前)
offsetX関係なし:event.targetとの相対位置
(正式前)
elementgetBoundingClientRect ()ビューポート
document.elementFromPoint() ドキュメント
getClientRects()ビューポート
scrollLeft関係なし  表示位置のずれ
(ある意味要素のビューポートサイズが widthで、
その中のスクロール位置が scrollLeft、要素の大きさが clientWidth)
clientLeft関係なし  padding edge から border edgeまでの横幅のずれ😰
(パディングから外側のマージンへの距離であるためボーダー幅)
element.clientLeft - Web API インターフェイス | MDN

要素の幅と高さ

座標系ではないが、スクロールや位置情報において関連して重要で、よく使うので、補足としてまとめてみた。

オブジェクト
サイズの取得
取得方法
screenwindow.screen.width
window.screen.availWidth
Screen - Web API インターフェイス | MDN
windowwindow.outerWidth  (ウインドウ外観)
/  設定は reseizeTo()  ポップアップ画面だけ
window.resizeBy()は同様ですが差分を指定します。
window.innerWidth (ウインドウ内)
elementelement.getBoundingClientRect().width
documentdocument.documentElement.getBoundingClientRect().width

座標変換

上記までは、どの座標系を中心に扱った。ここでは、よくあるプログラミング時のユースケースとして、座標変換や処理内容を中心としたよくあるパターンを検討して書いてみた。

ローカル/ドキュメント座標とビューポート座標

ウインドウの左上の位置(ローカル座標)が scrollXとなるのでその基準点の相対差分で求める。

スクロール位置と、マウスクリック位置と、要素(element)の位置の相互比較または変換のケース。

window.scrollX + mouseevent.clientX = element.pageX
(座標系から考えると不自然だが)
→要素のウインドウ位置を求めるとき :
event.clientX(ビューポート) = element.pageX(ドキュメント) - window.scrollX(ビューポート差分)

  • ドキュメント→ビューポート: window.scrollX を引く
  • ビューポート→スクリーン:  window.screenX を足す。

よくありそうな使用例

親子要素の位置の関係性

ビューポート座標(ウインドウ内の相対位置)が
element.getBoundingClientRect() で分かるので、
parentNode など使って、取得した値の差異を求めればいい。

スクロール位置 window.scrollXを足せば、ローカル座標(ドキュメント内)に変換できる。

スクロール位置

おそらく使う側として意図するところは見ている範囲という意味なので、window.scrollX, scrollYで取得し、 window.scroll()で設定する。

document.body.scrollTop/Leftがほぼ同じ値として使えるが、
本質的には違うものなのでずれるときがある。
例えば width(表示の大きさ) < clientWidth(element/要素の大きさ) の時は
入りきらないのでclient描画領域として over-flow 等の処理や大きさの考慮が必要。😰    bodyのスクロール位置の他に、内部の表示位置のずれもある。

マウス

event.clientX は ビューポート座標。つまり見た目の左上からの座標。element要素との比較には変換が必要になる。
要素との比較で楽するのであればevent.targetは ElementなのでgetBoundClientRect()を使えば、そのまま比較できる。

ただしイベントによっては、
event.target や event.currentTargetの指すものが nullになったり、
別の要素となることもある。
だから、機械的にeventであるからとせず、
しっかり状況を理解しマニュアルで意味するところを確認する必要がある。
※親要素の場合とか、ドラッグ先とか、イベント生成元とかケースが違う。😰

マウスイベント

clientX/Y を使う。

ちなみにpageX , offsetX などは正式ではないので、使用しないこと。
MouseEvent - Web API インターフェイス | MDN

スクロールバーの幅

スクロールバーの幅を正確に求めたくなるが、どうやっても求められない。スクロールバーはelement要素(document含む)が表示しきれない時に、自動で表示されるものだから。例えば縦が表示しきれない時に、内部構造的にはブラウザ内ではなく、bodyの横にスクロールバーが出る。

Element - Web APIs | MDN

一応ほかの情報源もリストアップ。

clientWidthから求めようとしているが、環境によるらしいので
16pxあたりの決め打ちでまずまずいいと思う。

document.body.clientWidth は幅100%表示したときに、内部を最大値まで表示していてくれるので、スクロールバーがあっても100%表示できた時
その前提で window.innerWidthとの差分がスクロールバーの幅になっているように見えるときがある。😰。例えばbodyが 画面サイズの10倍であれば body.clientWidthはその分大きくなるので window.innerWidthより大きい。

要素のscroll と client

先ほどのスクロールバーのように、実はスクロールバーはブラウザに属しているのではなく、各要素(Body , div...)の内部に表示されている。そして要素の幅の分表示サイズが取れない場合、内部にスクロール要素が追加され、スクロールの表示差分が0以外となってくる。

elemetのclientという概念の範囲は その Box全体の大きさを表す。つまり100%表示されていれば、見た目のサイズと等しくなる。一部しか表示されていない時に表示外に飛び出すイメージ。 (widthを考えれば意味が分かってくる。)
scrollの位置は0になるが、一番左上角からのスクロール量に似た値が取得できる。

clientWidth(実態)  > width (表示上のサイズ) と scrollY (相対的な表示のずれ位置)の考慮で関係性がわかる。

clientWidthは 要素のclientの幅。これはスクロールバーを除いたサイズではなく、 例えば幅が100%内に治まっているとき、ウインドウのスクロールバーを除いたサイズに見える。
一方ウインドウの幅は 文字通りなのでスクロールバーを除いたサイズではなく、別の基準の値。
body.clientWidthと window.innerWidthは全くの別物

つまり、スクロールバーの幅は、指定されていなければ暗に推測するぐらいしかできないっぽい。

大きい要素 TextArea , DIVの中身を詳しく解析したい時

以下の関数でチクチクやっていくしかない。
※ビューポイント座標系

Element.getClientRects() - Web APIs | MDN

指定した位置で要素を取得

document.elementFromPoint(x,y);

※ドキュメント座標系
document.elementFromPoint - Web API インターフェイス | MDN

Delicious にシェア
Digg にシェア
reddit にシェア
LinkedIn にシェア
LINEで送る
email this
Pocket

561 views.



コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です