2026年7月5日

座標

要素を移動させるためには、座標系の仕組みを理解しておく必要があります。

ほとんどの JavaScript メソッドは2つの座標系のいずれかを扱います:

  1. ウィンドウ相対座標position:fixedと同様で、ウィンドウの上端/左端を基準として計算されます。
    • これらの座標はclientX/clientYと表記します。このような名前の理由については、イベントプロパティについて学習する際に明らかになります。
  2. ドキュメント座標 – ドキュメントルートにおけるposition:absoluteと同様で、ドキュメントの上端/左端を基準として計算されます。
    • これらの座標はpageX/pageYと表記します。

ページを一番最初までスクロールして、ウィンドウの左上端がドキュメントの左上端と完全に一致した状態であれば、これらの座標は互いに等しくなります。しかし、ドキュメントがスクロールされると、要素がウィンドウ内を移動するため、要素のウィンドウ相対座標は変化しますが、ドキュメント相対座標は変わりません。

この画像では、ドキュメント内の特定の一点を、スクロール前(左)とスクロール後(右)の座標で示しています。

ドキュメントがスクロールされた場合:

  • pageY – ドキュメント相対座標は変化しません。これはドキュメントの最上部(すでにスクロールアウトした部分)を基準に数えられています。
  • clientY – ウィンドウ相対座標は変化します(矢印が短くなっています)。同じ点がウィンドウの最上部に近づいたためです。

要素の座標取得: getBoundingClientRect()

elem.getBoundingClientRect()メソッドは、elemを囲った最小の長方形のウィンドウ座標を、組み込みの DOMRect クラスのオブジェクトとして返します。

DOMRectの主要なプロパティ:

  • x/y – ウィンドウに対する、長方形の原点のX/Y座標。
  • width/height – 長方形の幅/高さ(負の値も可)。

さらに、派生したプロパティもあります:

  • top/bottom – 長方形の上端/下端のY座標。
  • left/right – 長方形の左端/右端のX座標。

例えば、このボタンをクリックして、そのウィンドウ座標を見てください。

繰り返しページをスクロールしていると、ウィンドウ内のボタンの位置が変化するにつれて、そのウィンドウ相対座標(縦方向にスクロールした場合は y/top/bottom 座標)も変化することに気づくでしょう。

以下がelem.getBoundingClientRect()の出力の図です:

ご覧のとおり、x/ywidth/heightで長方形を完全に表しています。これらから、派生プロパティを簡単に計算できます:

  • left = x
  • top = y
  • right = x + width
  • bottom = y + height

注意点:

  • 座標は10.5のような小数になる場合があります。これは正常で、ブラウザは内部で計算に小数を使用しています。style.left/topを設定する際に、小数を丸める必要はありません。
  • 座標が負の値になることもあります。例えば、ページをスクロールしてelemがウィンドウの上部にある場合、elem.getBoundingClientRect().topは負の値になります。
派生プロパティはなぜ必要ですか? x/yが存在するのに、なぜtop/leftが存在するのですか?

数学的には、長方形は開始点(x, y)と方向ベクトル(width, height)によって一意に定義されます。したがって、追加の派生プロパティは利便性のためのものです。

技術的には、width/heightが負の値になることも可能であり、これにより「方向性を持った」長方形(例えば、適切にマークされた開始点と終了点を持つマウスの選択範囲など)を表現できます。

width/heightの値が負の場合、長方形が右下端から始まり、左上に向かって「伸びる」ことを意味します。

以下に、負のwidthheightを持つ長方形(例:width=-200height=-100)を示します:

ご覧の通り、このような場合、left/topx/yと等しくありません。

ただし、実際にはelem.getBoundingClientRect()は常に正のwidth/heightを返します。ここで負のwidth/heightに言及するのは、これらの一見重複しているプロパティが実際には重複していないことを理解していただくためです。

Internet Explorer:x/yのサポートなし

Internet Explorerは歴史的な理由からx/yプロパティをサポートしていません。

したがって、ポリフィルを作成する(DomRect.prototypeにゲッターを追加する)か、単にtop/leftを使用することができます。なぜなら、width/heightが正の値である場合、これらは常にx/yと等しいからです。特にelem.getBoundingClientRect()の結果がこれに該当します。

座標 right/bottom はCSSのpositionプロパティとは異なります

ウィンドウ相対座標とCSSのposition:fixedの間には、明白な類似点があります。

しかし、CSSの配置において、rightプロパティは右端からの距離を意味し、bottomプロパティは下端からの距離を意味します。

上記の図を見れば分かるように、JavaScriptではそうではありません。これらの座標も含め、すべてのウィンドウ座標は左上端から数えられます。

elementFromPoint(x, y)

document.elementFromPoint(x, y) の呼び出しは、ウィンドウ座標 (x, y) で最もネストされた要素を返します。

構文は次の通りです:

let elem = document.elementFromPoint(x, y);

例えば、以下のコードは、現在ウィンドウの中央にある要素のタグを強調表示して出力します。:

let centerX = document.documentElement.clientWidth / 2;
let centerY = document.documentElement.clientHeight / 2;

let elem = document.elementFromPoint(centerX, centerY);

elem.style.background = "red";
alert(elem.tagName);

ウィンドウ座標を使うので、要素は現在のスクロール位置に応じて異なります。

ウィンドウ外の座標の場合、elementFromPointnull を返します。

メソッド document.elementFromPoint(x,y)(x,y) が可視領域にある場合にのみ動作します。

もし座標のいずれかが負の値であるか、ウィンドウの幅/高さを超えている場合、null を返します。

チェックを怠ると、以下のような典型的なエラーが発生する可能性があります:

let elem = document.elementFromPoint(x, y);
// 座標がウィンドウ外の場合、elem = null
elem.style.background = ''; // エラー!

「fixed」配置の使用

ほとんどの場合、何かを配置するには座標が必要です。

要素の近くに何かを表示するには、getBoundingClientRectを使用してその座標を取得し、次にCSSのpositionleft/top(またはright/bottom)を組み合わせて使用します。

例えば、以下のcreateMessageUnder(elem, html)関数は、elemの下にメッセージを表示します。

let elem = document.getElementById("coords-show-mark");

function createMessageUnder(elem, html) {
  // メッセージ要素の作成
  let message = document.createElement('div');
  // ここでは、スタイルにCSSクラスを使う方が良いです
  message.style.cssText = "position:fixed; color: red";

  // 座標の設定, "px" を忘れないこと!
  let coords = elem.getBoundingClientRect();

  message.style.left = coords.left + "px";
  message.style.top = coords.bottom + "px";

  message.innerHTML = html;

  return message;
}

// 使用例:
// ドキュメントに5秒間追加する
let message = createMessageUnder(elem, 'Hello, world!');
document.body.append(message);
setTimeout(() => message.remove(), 5000);

実行するにはボタンをクリックしてください:

このコードは、メッセージを左、右、下に表示したり、CSSアニメーションを適用して「フェードイン」させたりするように修正できます。要素のすべての座標とサイズが分かっているため、これは簡単です。

しかし、重要な点に注意してください。ページをスクロールすると、メッセージがボタンから離れていきます。

その理由は明白です。メッセージ要素がposition:fixedに依存しているため、ページがスクロールしてもウィンドウの同じ位置に留まろうとするからです。

これを変えるには、ドキュメント基準の座標とposition:absoluteを使用する必要があります。

ドキュメント座標

ドキュメント相対座標は、ウィンドウではなくドキュメントの左上端から始まります。

CSSでは、ウィンドウ座標はposition:fixedに対応し、ドキュメント座標はそれに近いposition:absoluteに対応します。

position:absolutetop/left を使用すると、ドキュメント内の特定の位置に要素を配置し、ページをスクロールしてもその位置に留まるようにすることができます。ただし、そのためにはまず正しい座標が必要です。

要素のドキュメント座標を取得するための標準メソッドはありません。しかし、それを記述するのは簡単です。

これら2つの座標系は、以下の式で結びつけられます:

  • pageY = clientY + ドキュメントのスクロールアウトした垂直部分の高さ。
  • pageX = clientX + ドキュメントのスクロールアウトした水平部分の幅。

getCoords(elem)関数は、elem.getBoundingClientRect()からウィンドウ座標を取得し、現在のスクロール量を加算します:

// 要素のドキュメント座標を取得
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
  };
}

もし上記の例でこれをposition:absoluteで使用した場合、メッセージはスクロールしても要素の近くに留まり続けます。

変更されたcreateMessageUnder関数は以下の通りです:

function createMessageUnder(elem, html) {
  let message = document.createElement('div');
  message.style.cssText = "position:absolute; color: red";

  let coords = getCoords(elem);

  message.style.left = coords.left + "px";
  message.style.top = coords.bottom + "px";

  message.innerHTML = html;

  return message;
}

サマリ

ページ上のあらゆる点は、座標を持っています:

  1. ウィンドウに相対的 – elem.getBoundingClientRect()
  2. ドキュメントに相対的 – elem.getBoundingClientRect() + 現在のページスクロール

ウィンドウ座標はposition:fixedで使用するのが適しており、ドキュメント座標はposition:absoluteで使用するのが適しています。

どちらの座標系も長所と短所を持っており、CSS の position absolutefixed のように、どちらか一方が必要なときがあります。

タスク

重要性: 5

下の iframe では、緑の “フィールド” を持つドキュメントがあります。

矢印でポイントされた角のウィンドウ座標を見つけるために、JavaScript を使用してください。

便宜のために、ドキュメント上に実装している小さな機能があります。任意の場所をクリックすると、そこの座標を表示します。

あなたのコードは次のウィンドウ座標を取得するために DOM を使う必要があります:

  1. 左上の外部の角(それはシンプルです)
  2. 右下の外部の角(これもシンプルです)
  3. 左上の内部の角(少し難しいです)
  4. 右下の内部の角(いくつか方法があり、1つを選んでください)

あなたが計算する座標は、マウスクリックで返却されるものと同じであるべきです。

P.S. コードは要素が別のサイズやボーダーの場合でも動作する必要があります。任意の固定値で束縛しないでください。

タスクのためのサンドボックスを開く

外部の角

外部の角は基本的に elem.getBoundingClientRect() から取得したものです。

左上の角 answer1 と、右下の角 answer2 の座標は:

let coords = elem.getBoundingClientRect();

let answer1 = [coords.left, coords.top];
let answer2 = [coords.right, coords.bottom];

左上の内部の角

それはボーダーの幅が外部の角とは異なります。その距離を取得する信頼できる方法は clientLeft/clientTop です。:

let answer3 = [coords.left + field.clientLeft, coords.top + field.clientTop];

右下の内部の角

我々のケースでは、外部の座標からボーダーのサイズを減算する必要があります。

CSS の方法を使用することができます:

let answer4 = [
  coords.right - parseInt(getComputedStyle(field).borderRightWidth),
  coords.bottom - parseInt(getComputedStyle(field).borderBottomWidth)
];

代わりの方法は clientWidth/clientHeight を左上の角の座標に足す方法です。おそらくこれはより良い方法でしょう。:

let answer4 = [
  coords.left + elem.clientLeft + elem.clientWidth,
  coords.top + elem.clientTop + elem.clientHeight
];

サンドボックスで解答を開く

重要性: 5

要素 anchor の 上 ("top"), 右 ("right") または下 ("bottom") のいずれかにある position に応じて elem を配置する関数 positionAt(anchor, position, elem) を作成してください。

それを使って、クラス "note" を持つ要素と、anchor の近くの指定された位置にテキスト html を表示する関数 showNote(anchor, position, html) を作成します。

次のようなノートを表示します:

P.S. ノートはこのタスクでは position:fixed を持たなければなりません。

タスクのためのサンドボックスを開く

このタスクは、座標を正しく計算するだけで済みます。詳細についてはコードを見てください。

注意: offsetHeight や他のプロパティを読むために要素はドキュメント上になければなりません。 隠れた (display:none) またはドキュメント外の要素はサイズがありません。

サンドボックスで解答を開く

重要性: 5

ノートが position:fixed の代わりに position:absolute を使うよう、 前のタスク の解決策を修正してください。

それはページスクロール時に、要素から “離れていく” のを防ぎます。

そのタスクの解決策を出発点にしてください。スクロールをテストするために、スタイル <body style="height: 2000px"> を追加してください。

解法は実際には非常にシンプルです:

  • .note に、position:fixed の代わりに position:absolute を CSS で使ってください。
  • ドキュメント相対座標を取得するために、チャプター 座標 の関数 getCoords() を使ってください。

サンドボックスで解答を開く

重要性: 5

前のタスク 要素の近くにノートを表示する(absolute) を拡張してください: 関数 positionAt(anchor, position, elem) に、 anchor の内側に elem を挿入するよう教えてください。

position のための新しい値:

  • top-out, right-out, bottom-out – 前と同じように動作し、elemanchor の上/右/下に挿入します。
  • top-in, right-in, bottom-inanchor の内側に elem を挿入します。: 上/右/下端に取り付けます。

例:

// blockquote の上にノートを表示します
positionAt(blockquote, "top-out", note);

// blockquote の内側の先頭にノートを表示します
positionAt(blockquote, "top-in", note);

結果:

ソースコードとして、タスク 要素の近くにノートを表示する(absolute) の解決策を使ってください。

チュートリアルマップ

コメント

コメントをする前に読んでください…
  • 自由に記事への追加や質問を投稿をしたり、それらに回答してください。
  • 数語のコードを挿入するには、<code> タグを使ってください。複数行の場合は <pre> を、10行を超える場合にはサンドボックスを使ってください(plnkr, JSBin, codepen…)。
  • 記事の中で理解できないことがあれば、詳しく説明してください。