Zenn
😑

JSでの主な基準のサイズ・要素のサイズと座標・ポインターの座標取得まとめメモ

2025/02/07に公開3
8


要素のサイズやポインターの座標など色々プロパティがありますが、その時々に調べて使用していました。しかしその作業が非常に煩わしいので、いい加減全体的にどうなのか理解するため&見返し用に纏めたメモです。

前提

Google Chromeで値確認。この記事で使用する単語の定義は以下。

  • ドキュメント
    • margin,border,paddingの設定がないHTMLHtmlElement
    • document.documentElementの内容が上記であると見なす
  • コンテンツ全体
    • HTMLBodyElement
    • document.bodyの内容が上記であると見なす
  • ビューポート
    • ブラウザのドキュメント表示領域
  • スクリーン
    • モニターの表示領域

主な基準のサイズ

大体の関係図


basis_size

確認用コード

code
<!DOCTYPE html>
<html>
<head>
  <style>
    body {
      margin: 10px;
      border: solid;
      border-width: 5px;
      padding: 7px;
    }
  </style>
</head>
<body>
  <button type="button" onclick="refresh()">refresh</button>
  <h4>window</h4>
  <ul>
    <li>inner: <span id="wiw"></span> / <span id="wih"></span></li>
    <li>outer: <span id="wow"></span> / <span id="woh"></span></li>
    <li>scroll: <span id="wsw"></span> / <span id="wsh"></span></li>
  </ul>
  <h4>html</h4>
  <ul>
    <li>offset: <span id="how"></span> / <span id="hoh"></span></li>
    <li>client: <span id="hcw"></span> / <span id="hch"></span></li>
    <li>scroll: <span id="hsw"></span> / <span id="hsh"></span></li>
    <li>bcrect: <span id="hgw"></span> / <span id="hgh"></span></li>
  </ul>
  <h4>body</h4>
  <ul>
    <li>offset: <span id="bow"></span> / <span id="boh"></span></li>
    <li>client: <span id="bcw"></span> / <span id="bch"></span></li>
    <li>scroll: <span id="bsw"></span> / <span id="bsh"></span></li>
    <li>bcrect: <span id="bgw"></span> / <span id="bgh"></span></li>
  </ul>
  <ul>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>test</li>
    <li>testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest</li>
  <ul>
  <script>
    function refresh() {
      const rectHtml = document.documentElement.getBoundingClientRect();
      const rectBody = document.body.getBoundingClientRect();
      getDOM("wiw").textContent = window.innerWidth;
      getDOM("wih").textContent = window.innerHeight;
      getDOM("wow").textContent = window.outerWidth;
      getDOM("woh").textContent = window.outerHeight;
      getDOM("wsw").textContent = window.scrollX;
      getDOM("wsh").textContent = window.scrollY;
      getDOM("how").textContent = document.documentElement.offsetWidth;
      getDOM("hoh").textContent = document.documentElement.offsetHeight;
      getDOM("hcw").textContent = document.documentElement.clientWidth;
      getDOM("hch").textContent = document.documentElement.clientHeight;
      getDOM("hsw").textContent = document.documentElement.scrollWidth;
      getDOM("hsh").textContent = document.documentElement.scrollHeight;
      getDOM("hgw").textContent = rectHtml.width;
      getDOM("hgh").textContent = rectHtml.height;
      getDOM("bow").textContent = document.body.offsetWidth;
      getDOM("boh").textContent = document.body.offsetHeight;
      getDOM("bcw").textContent = document.body.clientWidth;
      getDOM("bch").textContent = document.body.clientHeight;
      getDOM("bsw").textContent = document.body.scrollWidth;
      getDOM("bsh").textContent = document.body.scrollHeight;
      getDOM("bgw").textContent = rectBody.width;
      getDOM("bgh").textContent = rectBody.height;
    }
    function getDOM(id) {
      return document.querySelector(`#${id}`);
    }
  </script>
</body>
</html>

ドキュメント

  • HTMLHtmlElementインターフェース (document.documentElement)
    • 以下のプロパティは整数に丸められた値が取得される
    • offsetWidth/Heightの小数点以下を含む値は簡単に取得可能
      • .getBoundingClientRect().width/heightで取得
プロパティ メモ
offsetWidth/Height overflow分を含まない全体のサイズ
clientWidth/Height スクロールバーを含まないビューポートサイズ
scrollWidth/Height 全体のサイズ 最小値はclientWidth/Height

コンテンツ全体

  • HTMLBodyElementインターフェース (document.body)
    • 以下のプロパティは整数に丸められた値が取得される
    • offsetWidth/Heightの小数点以下を含む値は簡単に取得可能
      • .getBoundingClientRect().width/heightで取得
プロパティ メモ
offsetWidth/Height marginを含まない全体のサイズ
clientWidth/Height marginとborderを含まない全体のサイズ
scrollWidth/Height 使用しないのが無難 (overflowを含むがmarginを含まない)

ビューポート

  • Windowインターフェース (window)
プロパティ メモ
innerWidth/Height ビューポートのサイズ
outerWidth/Height ブラウザのウィンドウサイズ
scrollX/Y スクロールされた量

スクリーン

  • Screenインターフェース (window.screen)
プロパティ メモ
width/height モニターの解像度のイメージ
availWidth/Height タスクバー等を除いたモニターの表示領域

要素のサイズ

大体の関係図


element_size

確認用コード

code
<!DOCTYPE html>
<html>
<head>
  <style>
    #parent {
      position: relative;
    }
    #main {
      position: absolute;
      left: 50px;
      top: 50px;
      margin: 10px;
      border: solid;
      border-width: 5px;
      padding: 7px;
      width: 250px;
      height: 200px;
      overflow: auto;
      transform: rotate(var(--angle));
      --angle: 0deg;
    }
  </style>
</head>
<body>
  <button type="button" onclick="refresh()">refresh</button>
  <button type="button" onclick="rotate()">rotate</button>
  <h4>element</h4>
  <ul>
    <li>offset: <span id="offw"></span> / <span id="offh"></span></li>
    <li>client: <span id="cliw"></span> / <span id="clih"></span></li>
    <li>scroll: <span id="scrw"></span> / <span id="scrh"></span></li>
    <li>bcrect: <span id="bcrw"></span> / <span id="bcrh"></span></li>
  </ul>
  <div id="parent">
    <div id="main">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum at laoreet magna.
Aliquam erat volutpat. Praesent molestie, dolor ut eleifend aliquam, mi ligula ultrices sapien, quis cursus
neque dui nec risus. Duis tincidunt lobortis purus eu aliquet. Quisque in dignissim magna. Aenean ac lorem at
velit ultrices consequat. Nulla luctus nisi ut libero cursus ultrices. Pellentesque nec dignissim enim. Phasellus
ut quam lacus, sed ultricies diam. Vestibulum convallis rutrum dolor, sit amet egestas velit scelerisque id.
Proin non dignissim nisl. testtesttesttesttesttesttesttesttest Sed mi odio, ullamcorper eget mattis id, malesuada vitae libero. Integer dolor lorem,
mattis sed dapibus a, faucibus id metus. Duis iaculis dictum pulvinar. In nisi nibh, dapibus ac blandit at, porta
at arcu. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Praesent
dictum ipsum aliquet erat eleifend sit amet sollicitudin felis tempus. Aliquam congue cursus venenatis. Maecenas
luctus pellentesque placerat. Mauris nisl odio, condimentum sed fringilla a, consectetur id ligula. Praesent sem
sem, aliquet non faucibus vitae, iaculis nec elit. Nullam volutpat, lectus et blandit bibendum, nulla lorem congue
turpis, ac pretium tortor sem ut nibh. Donec vel mi in ligula hendrerit sagittis. Donec faucibus viverra fermentum.
    </div>
  </div>

  <script>
    function refresh() {
      const el = getDOM("main");
      const rect = el.getBoundingClientRect();
      getDOM("offw").textContent = el.offsetWidth;
      getDOM("offh").textContent = el.offsetHeight;
      getDOM("cliw").textContent = el.clientWidth;
      getDOM("clih").textContent = el.clientHeight;
      getDOM("scrw").textContent = el.scrollWidth;
      getDOM("scrh").textContent = el.scrollHeight;
      getDOM("bcrw").textContent = rect.width;
      getDOM("bcrh").textContent = rect.height;
    }
    function getDOM(id) {
      return document.querySelector(`#${id}`);
    }
    function rotate() {
      const el = getDOM("main");
      const current = getComputedStyle(el).getPropertyValue("--angle");
      const angle = current === "0deg" ? "45deg" : current === "45deg" ? "90deg" : "0deg";
      el.style.setProperty("--angle", angle);
    }
  </script>
</body>
</html>

各プロパティ

  • 以下のプロパティは整数に丸められた値が取得される
  • 通常時、offsetWidth/Heightの小数点以下を含む値は簡単に取得可能
    • .getBoundingClientRect().width/heightで取得
プロパティ 所属 メモ
offsetWidth/Height HTMLElement main+padding+border(+scrollbar)
clientWidth/Height Element main+padding
scrollWidth/Height Element overflowを含むmain+padding

getBoundingClientRect(DOMRect)の違い

  • CSSのtransform後の状態が値に反映される
    • offset,client,scroll系プロパティは値が変化しない
    • DOMRectのみ値が変化する

要素の座標

大体の関係図


element_position

確認用コード

code
<!DOCTYPE html>
<html>
<head>
  <style>
    #parent {
      position: relative;
    }
    #main {
      position: absolute;
      left: 50px;
      top: 50px;
      margin: 10px;
      border: solid;
      border-width: 5px;
      padding: 7px;
      width: 200px;
      height: 200px;
      overflow: auto;
    }
  </style>
</head>
<body>
  <button type="button" onclick="refresh()">refresh</button>
  <h4>element</h4>
  <ul>
    <li>offset: <span id="offl"></span> / <span id="offt"></span></li>
    <li>client: <span id="clil"></span> / <span id="clit"></span></li>
    <li>scroll: <span id="scrl"></span> / <span id="scrt"></span></li>
    <li>bcrect: <span id="bcrl"></span> / <span id="bcrt"></span></li>
  </ul>
  <div id="parent">
    <div id="main">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum at laoreet magna.
Aliquam erat volutpat. Praesent molestie, dolor ut eleifend aliquam, mi ligula ultrices sapien, quis cursus
neque dui nec risus. Duis tincidunt lobortis purus eu aliquet. Quisque in dignissim magna. Aenean ac lorem at
velit ultrices consequat. Nulla luctus nisi ut libero cursus ultrices. Pellentesque nec dignissim enim. Phasellus
ut quam lacus, sed ultricies diam. Vestibulum convallis rutrum dolor, sit amet egestas velit scelerisque id.
Proin non dignissim nisl. testtesttesttesttesttesttesttesttest Sed mi odio, ullamcorper eget mattis id, malesuada vitae libero. Integer dolor lorem,
mattis sed dapibus a, faucibus id metus. Duis iaculis dictum pulvinar. In nisi nibh, dapibus ac blandit at, porta
at arcu. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Praesent
dictum ipsum aliquet erat eleifend sit amet sollicitudin felis tempus. Aliquam congue cursus venenatis. Maecenas
luctus pellentesque placerat. Mauris nisl odio, condimentum sed fringilla a, consectetur id ligula. Praesent sem
sem, aliquet non faucibus vitae, iaculis nec elit. Nullam volutpat, lectus et blandit bibendum, nulla lorem congue
turpis, ac pretium tortor sem ut nibh. Donec vel mi in ligula hendrerit sagittis. Donec faucibus viverra fermentum.
    </div>
  </div>

  <script>
    function refresh() {
      const el = getDOM("main");
      const rect = el.getBoundingClientRect();
      getDOM("offl").textContent = el.offsetLeft;
      getDOM("offt").textContent = el.offsetTop;
      getDOM("clil").textContent = el.clientLeft;
      getDOM("clit").textContent = el.clientTop;
      getDOM("scrl").textContent = el.scrollLeft;
      getDOM("scrt").textContent = el.scrollTop;
      getDOM("bcrl").textContent = rect.left;
      getDOM("bcrt").textContent = rect.top;
    }
    function getDOM(id) {
      return document.querySelector(`#${id}`);
    }
  </script>
</body>
</html>

各プロパティ

  • clientLeft/Topは整数に丸められた値が取得される
  • elは対象の要素を表す
  • rectel.getBoundingClientRect()DOMRectを表す
プロパティ 原点 位置 メモ
el.offsetLeft/Top el.offsetParent elborder始点 親要素からの相対位置
el.clientLeft/Top elborder始点 elpadding始点 borderの幅
el.scrollLeft/Top elpadding始点 表示領域始点 スクロール幅
rect.left/top(rect.x/y) ビューポート elborder始点 ビューポートからの相対位置

ポインターの座標

大体の関係図


pointer_position

確認用コード

code
<!DOCTYPE html>
<html>
<head>
  <style>
    #parent {
      margin: 50px;
      border: solid;
      border-width: 1px;
    }
    #main {
      margin: 30px;
      border: solid;
      border-width: 5px;
      padding: 7px;
      width: 400px;
      height: 300px;
    }
  </style>
</head>
<body>
  <br>
  <br>
  <br>
  <br>
  <br>
  <br>
  <br>
  <br>
  <br>
  <br>
  <button type="button" onclick="start()">start</button>
  <button type="button" onclick="startThrottle()">throttle</button>
  <button type="button" onclick="end()">end</button>
  <h4>element</h4>
  <ul>
    <li>offset: <span id="offx"></span> / <span id="offy"></span></li>
    <li>client: <span id="clix"></span> / <span id="cliy"></span></li>
    <li>screen: <span id="scrx"></span> / <span id="scry"></span></li>
    <li>page: <span id="pgx"></span> / <span id="pgy"></span></li>
    <li>mvmt: <span id="mvx"></span> / <span id="mvy"></span></li>
    <li>manual: <span id="manx"></span> / <span id="many"></span></li>
  </ul>
  <div id="parent">
    <div id="main">
    </div>
  </div>

  <script>
    const fnThrottle = throttle(100,update);
    let prev = { x: 0, y: 0 };
    function start() {
      document.addEventListener("pointermove", update);
    }
    function startThrottle() {
      
      document.addEventListener("pointermove", fnThrottle);
    }
    function end() {
      document.removeEventListener("pointermove", update);
      document.removeEventListener("pointermove", fnThrottle);
      prev = { x: 0, y: 0 };
    }
    function updateThrottle(ev) {

    }
    function update(ev) {
      getDOM("offx").textContent = ev.offsetX;
      getDOM("offy").textContent = ev.offsetY;
      getDOM("clix").textContent = ev.clientX;
      getDOM("cliy").textContent = ev.clientY;
      getDOM("scrx").textContent = ev.screenX;
      getDOM("scry").textContent = ev.screenY;
      getDOM("pgx").textContent = ev.pageX;
      getDOM("pgy").textContent = ev.pageY;
      getDOM("mvx").textContent = ev.movementX;
      getDOM("mvy").textContent = ev.movementY;
      const manual = calc(ev);
      getDOM("manx").textContent = manual.x;
      getDOM("many").textContent = manual.y;
    }
    function getDOM(id) {
      return document.querySelector(`#${id}`);
    }
    function calc(ev) {
      const ret = { x: ev.screenX-prev.x, y: ev.screenY-prev.y };
      prev = { x: ev.screenX, y: ev.screenY };
      return ret;
    }
    function throttle(interval, fn) {
        let timer;
        let last = 0;
        const elapsed = () => Date.now() - last;
        const run = (args) => {
            fn.call(null, ...args);
            last = Date.now();
        };
        return (...args) => {
            if (!last) {
                run(args);
                return;
            }
            clearTimeout(timer);
            timer = setTimeout(() => {
                if (elapsed() >= interval) {
                    run(args);
                }
            }, interval - elapsed());
        };
    }
  </script>
</body>
</html>

各プロパティ

  • PointerEventインターフェース
プロパティ 原点 備考
offsetX/Y 直下要素 直下要素:document.elementFromPoint(ev.clientX,ev.clientY)
clientX/Y ビューポート 相対位置のイメージ
pageX/Y ドキュメント 絶対位置のイメージ
screenX/Y モニター OSのAPIをブラウザがバイパスしているイメージ

参考文献

8

Discussion

junerjuner

PointerEvent の というよりも MouseEvent のではあるのですが movementX movementY 便利ですよね。

https://w3c.github.io/pointerlock/#dom-mouseevent-movementx

scirexsscirexs

便利ですね!と言いたいのですが、私の用途ではmousemoveやpointermoveイベントは常にthrottlingさせているので使ったことがないのです・・・。
が、同じことを自分で計算することはあるので、もしかしたらthrottlingでも使えるかもと思い直し検証してみました。結果はダメでしたが良いきっかけになりました、ありがとうございます。

ログインするとコメントできます