diff --git a/2-ui/1-document/11-coordinates/article.md b/2-ui/1-document/11-coordinates/article.md index 0da4273062..2dc6374b51 100644 --- a/2-ui/1-document/11-coordinates/article.md +++ b/2-ui/1-document/11-coordinates/article.md @@ -1,64 +1,105 @@ # 座標 -要素を移動させるには、座標についてよく知っている必要があります。 +要素を移動させるためには、座標系の仕組みを理解しておく必要があります。 ほとんどの JavaScript メソッドは2つの座標系のいずれかを扱います: -1. ウィンドウ(もしくは別のビューポート)の 上/左 を基準にします -2. ドキュメントの 上/左 を基準とします +1. **ウィンドウ相対座標** - `position:fixed`と同様で、ウィンドウの上端/左端を基準として計算されます。 + - これらの座標は`clientX/clientY`と表記します。このような名前の理由については、イベントプロパティについて学習する際に明らかになります。 +2. **ドキュメント座標** - ドキュメントルートにおける`position:absolute`と同様で、ドキュメントの上端/左端を基準として計算されます。 + - これらの座標は`pageX/pageY`と表記します。 -違いを理解し、どのタイプがどこにあるかを理解することは重要です。 +ページを一番最初までスクロールして、ウィンドウの左上端がドキュメントの左上端と完全に一致した状態であれば、これらの座標は互いに等しくなります。しかし、ドキュメントがスクロールされると、要素がウィンドウ内を移動するため、要素のウィンドウ相対座標は変化しますが、ドキュメント相対座標は変わりません。 -[cut] +この画像では、ドキュメント内の特定の一点を、スクロール前(左)とスクロール後(右)の座標で示しています。 -## ウィンドウ座標: getBoundingClientRect + -ウィンドウ座標はウィンドウの左上端から始まります。 +ドキュメントがスクロールされた場合: +- `pageY` – ドキュメント相対座標は変化しません。これはドキュメントの最上部(すでにスクロールアウトした部分)を基準に数えられています。 +- `clientY` – ウィンドウ相対座標は変化します(矢印が短くなっています)。同じ点がウィンドウの最上部に近づいたためです。 -メソッド `elem.getBoundingClientRect()` はプロパティを持つオブジェクトとして `elem` に対するウィンドウ座標を返します。: +## 要素の座標取得: `getBoundingClientRect()` -- `top` -- 要素の上端の Y-座標, -- `left` -- 要素の左端の X-座標, -- `right` -- 要素の右端の X-座標, -- `bottom` -- 要素の下端の Y-座標. +`elem.getBoundingClientRect()`メソッドは、`elem`を囲った最小の長方形のウィンドウ座標を、組み込みの [DOMRect](https://www.w3.org/TR/geometry-1/#domrect) クラスのオブジェクトとして返します。 -このようになります: +`DOMRect`の主要なプロパティ: - +- `x/y` -- ウィンドウに対する、長方形の原点のX/Y座標。 +- `width/height` -- 長方形の幅/高さ(負の値も可)。 +さらに、派生したプロパティもあります: -ウィンドウ座標はドキュメントのスクロールアウト部分を考慮せず、ウィンドウの左上端から計算されたものになります。 - -言い換えると、ページをスクロールするとき、要素は上下に移動し、*そのウィンドウ座標は変わります*。これはとても重要です。 - +- `top/bottom` -- 長方形の上端/下端のY座標。 +- `left/right` -- 長方形の左端/右端のX座標。 ```online -ボタンをクリックしてウィンドウ座標を見てみてください: +例えば、このボタンをクリックして、そのウィンドウ座標を見てください。 - +
-もしページをスクロールすると、ボタン位置は代わり、ウィンドウ座標も同様に変わります。 +繰り返しページをスクロールしていると、ウィンドウ内のボタンの位置が変化するにつれて、そのウィンドウ相対座標(縦方向にスクロールした場合は `y/top/bottom` 座標)も変化することに気づくでしょう。 +``` + +以下が`elem.getBoundingClientRect()`の出力の図です: + + + +ご覧のとおり、`x/y`と`width/height`で長方形を完全に表しています。これらから、派生プロパティを簡単に計算できます: + +- `left = x` +- `top = y` +- `right = x + width` +- `bottom = y + height` + +注意点: + +- 座標は`10.5`のような小数になる場合があります。これは正常で、ブラウザは内部で計算に小数を使用しています。`style.left/top`を設定する際に、小数を丸める必要はありません。 +- 座標が負の値になることもあります。例えば、ページをスクロールして`elem`がウィンドウの上部にある場合、`elem.getBoundingClientRect().top`は負の値になります。 + +```smart header="派生プロパティはなぜ必要ですか? x/yが存在するのに、なぜtop/leftが存在するのですか?" +数学的には、長方形は開始点` (x, y)`と方向ベクトル` (width, height)`によって一意に定義されます。したがって、追加の派生プロパティは利便性のためのものです。 + +技術的には、`width/height`が負の値になることも可能であり、これにより「方向性を持った」長方形(例えば、適切にマークされた開始点と終了点を持つマウスの選択範囲など)を表現できます。 + +`width/height`の値が負の場合、長方形が右下端から始まり、左上に向かって「伸びる」ことを意味します。 + +以下に、負の`width`と`height`を持つ長方形(例:`width=-200`、`height=-100`)を示します: + + + +ご覧の通り、このような場合、`left/top`は`x/y`と等しくありません。 + +ただし、実際には`elem.getBoundingClientRect()`は常に正の`width/height`を返します。ここで負の`width/height`に言及するのは、これらの一見重複しているプロパティが実際には重複していないことを理解していただくためです。 ``` -また: +```warn header="Internet Explorer:x/yのサポートなし" +Internet Explorerは歴史的な理由から`x/y`プロパティをサポートしていません。 -- 座標は小数の場合があります。それは正常で、内部的にはブラウザは計算のためにそれを使用します。私たちは `style.position.left/top` へ設定するときにそれらを丸める必要はありません。ブラウザで小数は問題ありません。 -- 座標は負の値になる場合があります。例えば、ページが下にスクロールされ、`elem` の上端がウィンドウの上にある場合、`elem.getBoundingClientRect().top` は負の値になります。 -- Chromeような一部のブラウザでは、結果 `getBoundingClientRect` にプロパティ `width` と `height` も追加します。減算することでもそれらを取得することは可能です: `height=bottom-top`, `width=right-left`。 +したがって、ポリフィルを作成する(`DomRect.prototype`にゲッターを追加する)か、単に`top/left`を使用することができます。なぜなら、`width/height`が正の値である場合、これらは常に`x/y`と等しいからです。特に`elem.getBoundingClientRect()`の結果がこれに該当します。 +``` -```warn header="座標 右/下 は CSS プロパティとは異なります" -ウィンドウ座標と CSSの配置を比較すると、`position:fixed` との明らかな類似点があります -- ビューポートを基準とした位置も同じです。 +```warn header="座標 right/bottom はCSSのpositionプロパティとは異なります" +ウィンドウ相対座標とCSSの`position:fixed`の間には、明白な類似点があります。 -しかし、CSS では `right` プロパティは右端からの距離を意味し、`bottom` は -- 下端からの距離です。 +しかし、CSSの配置において、`right`プロパティは右端からの距離を意味し、`bottom`プロパティは下端からの距離を意味します。 -上の図を見た時、JavaScriptではそうでないことが分かります。すべてのウィンドウ座標は左上隅から数えられます。 +上記の図を見れば分かるように、JavaScriptではそうではありません。これらの座標も含め、すべてのウィンドウ座標は左上端から数えられます。 ``` ## elementFromPoint(x, y) @@ -71,7 +112,7 @@ function showRect(elem) { let elem = document.elementFromPoint(x, y); ``` -例えば、下のコードは今ウィンドウの中央にある要素のタグを強調表示し、出力します。: +例えば、以下のコードは、現在ウィンドウの中央にある要素のタグを強調表示して出力します。: ```js run let centerX = document.documentElement.clientWidth / 2; @@ -88,11 +129,9 @@ alert(elem.tagName); ````warn header="ウィンドウ外の座標の場合、`elementFromPoint` は `null` を返します。" メソッド `document.elementFromPoint(x,y)` は `(x,y)` が可視領域にある場合にのみ動作します。 -もしある座標が負の値またはウィンドウの幅/高さを超えている場合、`null` を返します。 - -ほとんどの場合、このような振る舞いは問題ではありませんが、それを心に留めておく必要があります。 +もし座標のいずれかが負の値であるか、ウィンドウの幅/高さを超えている場合、`null` を返します。 -これは、それをチェックしない場合に発生する可能性のある典型的なエラーです: +チェックを怠ると、以下のような典型的なエラーが発生する可能性があります: ```js let elem = document.elementFromPoint(x, y); @@ -103,13 +142,13 @@ elem.style.background = ''; // エラー! ``` ```` -## position:fixed を用いる +## 「fixed」配置の使用 -多くの場合、何かを配置するために座標を必要とします。CSS ではビューポートを基準として要素を配置するために、`left/top` (または `right/bottom`) と一緒に `position:fixed` を使います。 +ほとんどの場合、何かを配置するには座標が必要です。 -私たちは、`getBoundingClientRect` を使って要素の座標を取得し、その近くに何かを表示することができます。 +要素の近くに何かを表示するには、`getBoundingClientRect`を使用してその座標を取得し、次にCSSの`position`と`left/top`(または`right/bottom`)を組み合わせて使用します。 -例えば、下の関数 `createMessageUnder(elem, html)` は `elem` の下にメッセージを表示します。: +例えば、以下の`createMessageUnder(elem, html)`関数は、`elem`の下にメッセージを表示します。 ```js let elem = document.getElementById("coords-show-mark"); @@ -134,7 +173,7 @@ function createMessageUnder(elem, html) { } // 使用例: -// ドキュメント上に5秒間追加します +// ドキュメントに5秒間追加する let message = createMessageUnder(elem, 'Hello, world!'); document.body.append(message); setTimeout(() => message.remove(), 5000); @@ -146,67 +185,71 @@ setTimeout(() => message.remove(), 5000); ``` -このコードを変更して、メッセージを左、右、下に表示したり、"フェードイン" するための CSS アニメーションを適用することができます。私たちは要素のすべての座標とサイズを知っているので簡単にできます。 +このコードは、メッセージを左、右、下に表示したり、CSSアニメーションを適用して「フェードイン」させたりするように修正できます。要素のすべての座標とサイズが分かっているため、これは簡単です。 -しかし、重要な点に注意してください: ページがスクロールされたとき、メッセージはボタンから離れていきます。 +しかし、重要な点に注意してください。ページをスクロールすると、メッセージがボタンから離れていきます。 -理由は明らかです: メッセージ要素は `position:fixed` に依存しているので、ページがスクロールしている間、ウィンドウの同じ場所にい続けます。 +その理由は明白です。メッセージ要素が`position:fixed`に依存しているため、ページがスクロールしてもウィンドウの同じ位置に留まろうとするからです。 -変更するためには、ドキュメントベースの座標を使い、`position:absolute` を使う必要があります。 +これを変えるには、ドキュメント基準の座標と`position:absolute`を使用する必要があります。 -## ドキュメント座標 +## ドキュメント座標 -ドキュメント相対座標は、ウィンドウではなくドキュメントの左上端から始めます。 +ドキュメント相対座標は、ウィンドウではなくドキュメントの左上端から始まります。 -CSS では、ウィンドウ座標は `position:fixed` に対応する一方、ドキュメント座標は `position:absolute` に似ています。 +CSSでは、ウィンドウ座標は`position:fixed`に対応し、ドキュメント座標はそれに近い`position:absolute`に対応します。 -`position:absolute` と `top/left` を使うことで、ドキュメント上の特定の場所に何かを置くことができます。なので、ページのスクロール時にそこに残ることができます。しかし、最初に正しい座標が必要です。 +`position:absolute` と `top/left` を使用すると、ドキュメント内の特定の位置に要素を配置し、ページをスクロールしてもその位置に留まるようにすることができます。ただし、そのためにはまず正しい座標が必要です。 -分かりやすくするために、ウィンドウ座標 `(clientX,clientY)` とドキュメント座標 `(pageX,pageY)` を呼び出します。 +要素のドキュメント座標を取得するための標準メソッドはありません。しかし、それを記述するのは簡単です。 -ページがスクロールされていないとき、ウィンドウ座標とドキュメント座標はまったく同じです。それらのゼロの点も一致します: +これら2つの座標系は、以下の式で結びつけられます: +- `pageY` = `clientY` + ドキュメントのスクロールアウトした垂直部分の高さ。 +- `pageX` = `clientX` + ドキュメントのスクロールアウトした水平部分の幅。 - +`getCoords(elem)`関数は、`elem.getBoundingClientRect()`からウィンドウ座標を取得し、現在のスクロール量を加算します: -そして、スクロールをすると、`(clientX,clientY)` が変わります。なぜなら、それらはウィンドウに相対的なためです。しかし、`(pageX,pageY)` は同じままです。 - -これは縦スクロール後の同じページです: +```js +// 要素のドキュメント座標を取得 +function getCoords(elem) { + let box = elem.getBoundingClientRect(); - + return { + top: box.top + window.pageYOffset, + right: box.right + window.pageXOffset, + bottom: box.bottom + window.pageYOffset, + left: box.left + window.pageXOffset + }; +} +``` -- 要素は今ウィンドウの上部にあるので、ヘッダー `"From today's featured article"` の `clientY` は `0` になりました。 -- 水平スクロールをしなかったため、`clientX` は変わりませんでした。 -- 要素の `pageX` と `pageY` 座標は依然として同じです。なぜなら、それらはドキュメントに相対的だからです。 +もし上記の例でこれを`position:absolute`で使用した場合、メッセージはスクロールしても要素の近くに留まり続けます。 -## ドキュメント座標の取得 +変更された`createMessageUnder`関数は以下の通りです: -要素のドキュメント座標を取得するための標準メソッドはありません。しかし、簡単に書けます。 +```js +function createMessageUnder(elem, html) { + let message = document.createElement('div'); + message.style.cssText = "*!*position:absolute*/!*; color: red"; -2つの座標系は次の式で繋がれます: -- `pageY` = `clientY` + ドキュメントのスクロールアウトした垂直部分の高さ -- `pageX` = `clientX` + ドキュメントのスクロールアウトした水平部分の幅 + let coords = *!*getCoords(elem);*/!* -関数 `getCoords(elem)` は `elem.getBoundingClientRect()` からウィンドウ座標を取り、それらに現在のスクロールを加えます。: + message.style.left = coords.left + "px"; + message.style.top = coords.bottom + "px"; -```js -// 要素のドキュメント座標を取得 -function getCoords(elem) { - let box = elem.getBoundingClientRect(); + message.innerHTML = html; - return { - top: box.top + pageYOffset, - left: box.left + pageXOffset - }; + return message; } ``` ## サマリ -ページ上に任意の点は座標を持っています: +ページ上のあらゆる点は、座標を持っています: 1. ウィンドウに相対的 -- `elem.getBoundingClientRect()` 2. ドキュメントに相対的 -- `elem.getBoundingClientRect()` + 現在のページスクロール -ウィンドウ座標は `position:fixed` と一緒に使用するのが賢明で、ドキュメント座標は `position:absolute` と上手くやります。 +ウィンドウ座標は`position:fixed`で使用するのが適しており、ドキュメント座標は`position:absolute`で使用するのが適しています。 -どちらの座標系も "長所" と "短所" を持っており、CSS の `position` `absolute` と `fixed` のように、どちらか一方が必要なときがあります。 +どちらの座標系も長所と短所を持っており、CSS の `position` `absolute` と `fixed` のように、どちらか一方が必要なときがあります。 diff --git a/2-ui/1-document/11-coordinates/coordinates-negative.svg b/2-ui/1-document/11-coordinates/coordinates-negative.svg new file mode 100644 index 0000000000..4f2e78687c --- /dev/null +++ b/2-ui/1-document/11-coordinates/coordinates-negative.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/2-ui/1-document/11-coordinates/coordinates.svg b/2-ui/1-document/11-coordinates/coordinates.svg new file mode 100644 index 0000000000..261ff66966 --- /dev/null +++ b/2-ui/1-document/11-coordinates/coordinates.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/2-ui/1-document/11-coordinates/coords.png b/2-ui/1-document/11-coordinates/coords.png deleted file mode 100644 index 58fd533fba..0000000000 Binary files a/2-ui/1-document/11-coordinates/coords.png and /dev/null differ diff --git a/2-ui/1-document/11-coordinates/coords@2x.png b/2-ui/1-document/11-coordinates/coords@2x.png deleted file mode 100644 index c3469f4cde..0000000000 Binary files a/2-ui/1-document/11-coordinates/coords@2x.png and /dev/null differ diff --git a/2-ui/1-document/11-coordinates/document-and-window-coordinates-scrolled.svg b/2-ui/1-document/11-coordinates/document-and-window-coordinates-scrolled.svg new file mode 100644 index 0000000000..f03317f0c3 --- /dev/null +++ b/2-ui/1-document/11-coordinates/document-and-window-coordinates-scrolled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png b/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png deleted file mode 100644 index 39ddc31c3c..0000000000 Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png and /dev/null differ diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png b/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png deleted file mode 100644 index 52e555c490..0000000000 Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png and /dev/null differ diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png b/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png deleted file mode 100644 index 706b42c110..0000000000 Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png and /dev/null differ diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png b/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png deleted file mode 100644 index 83f31950c0..0000000000 Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png and /dev/null differ