getClientRects()とgetBoundingClientRect()の違いとその動作のまとめ

ワタシ、ニホンゴ ニガテ。ダカラ コレ ヨク イミ ワカラナイ。

Element.getClientRects()
    * 一行一行のボックス情報を取得する
Element.getBoundingClientRect
    * ボーダー辺で囲まれた領域のボックス情報を取得できる

getBoundingClientRect()はDrag&Dropを実装する際に使った事があったので、Rectが取得できるヤツだと認識してたけど、getClientRects()とか知らなかった。で、上記の説明を読んだのは良いんだけど、どう違うのかが理解できなかった。

なので、getClientRects()とgetBoundingClientRect()がどういうヤツなのかをメモを残しとく。なお、このメモはあくまでIE6での動作のまとめであって、Firefox3での動作なんて眼中にないのであしからず。

getBoundingClientRect()

ある要素のクライアント座標(画面スクロールに関わらず表示中の領域の左上が必ず(0, 0)になる)を取得する。ただし、左/上が2pxずれるバグ?あり。

動作例1

こんなHTML/スクリプトを用意。

<html>
<head>
<style type="text/css">
<!--
#hoge {
    width: 100px;
    height: 100px;
    background-color: red;
    position: absolute;
    left: 20px;
    top: 20px;
}
-->
</style>
<script type="text/javascript">
<!--
window.onload = function() {
    var elem = document.getElementById("hoge");
    var rect = elem.getBoundingClientRect();
    var buf = [];
    for(key in rect) {
        buf.push(key + " : " + rect[key]);
    }
    alert(buf.join("\n"));
}
-->
</script>
</head>
<body>
<div id="hoge"></div>
</body>
</html>

動かしてみる。
getBoundingClientRect_1
getBoundingClientRect_1 posted by (C)terurou

ちゃんと「2pxずれた」座標が取得できてますね!

動作例2

今度は画面をスクロールさせた状態で動かしてみる。

<html>
<head>
<style type="text/css">
<!--
#hoge {
    width: 100px;
    height: 100px;
    background-color: red;
    position: absolute;
    /* 座標を遥か彼方へ… */
    left: 1000px;
    top: 1000px;
}
-->
</style>
<script type="text/javascript">
<!--
window.onload = function() {
    document.onclick = function() {
        var elem = document.getElementById("hoge");
        var rect = elem.getBoundingClientRect();
        var buf = [];
        for(key in rect) {
            buf.push(key + " : " + rect[key]);
        }
        alert(buf.join("\n"));
    }
}
-->
</script>
</head>
<body>
<div id="hoge"></div>
</body>
</html>

で、画面をスクロールさせた状態で実行。
getBoundingClientRect_2
getBoundingClientRect_2 posted by (C)terurou

ちゃんとクライアント座標が取得できた。この場合もしっかりと「2pxずれ」が発生しますです。

getClientRects()

上手に説明できる日本語力/文章力がないので図示しながら。

こんなHTMLがあるとする。

<html>
<head>
<style type="text/css">
<!--
#hoge {
    width: 200px;
    background-color: #FDD;
    position: absolute;
    left: 20px;
    top: 20px;
}
#hogehoge {
    border: 1px solid red;
}
-->
</style>
</head>
<body>
<div id="hoge">
<p id="hogehoge">前書いた萌ディタを再起動するだけのAutoHotkeyスクリプトがma.laさんにクリップされてた。まだ萌ディタ使ってるって事かなぁ。</p>
</div>
</body>
</html>

このHTMLを画面表示するとこんな感じになる。

getClientRects_1
getClientRects_1 posted by (C)terurou

で、この画面の p#hogehoge に対してgetClientRects()してみる。

var elem = document.getElementById("hogehoge");
var rects = elem.getClientRects();

alert(rects.length);    // 5
alert(rects[0].top);    // 22
alert(rects[0].bottom); // 41
alert(rects[0].left);   // 22
alert(rects[0].right);  // 222

alert(rects[1].top);    // 41
alert(rects[2].top);    // 59
alert(rects[3].top);    // 77
alert(rects[4].top);    // 95

これじゃ判りづらいので、取得できた値を図示してみる。

getClientRects_2
getClientRects_2 posted by (C)terurou

なるほど、確かに「一行一行のボックス情報を取得する」ですねぇ…。特筆すべきところとしては、

  • 1行目のheightにborder-top-widthが含まれる。
  • 最終行のheightにborder-bottom-widthが含まれる。
  • やっぱり2pxずれる。
  • 取得できる座標はクライアント座標なので、スクロールさせた状態だと値が変わる。
getClientRects()の注意点

仕様なのかバグなのか判らんのですが、widthが指定されている要素に対してgetClientRects()すると、複数行のはずなのに1行として扱われてしまうっぽい。

<html>
<head>
<style type="text/css">
<!--
#hoge {
    width: 200px;
    background-color: #FDD;
    position: absolute;
    left: 20px;
    top: 20px;
}
-->
</style>
</head>
<body>
<div id="hoge">前書いた萌ディタを再起動するだけのAutoHotkeyスクリプトがma.laさんにクリップされてた。まだ萌ディタ使ってるって事かなぁ。</div>
</body>
</html>
var elem = document.getElementById("hoge");
var rects = elem.getClientRects();

alert(rects.length); // 1

予告

2pxずれるバグ?の原因/対処法についてはだいたい調査済みなので、今度書く予定。

追記

だいぶ時間が経っちゃったけど対策書いた。

IEの getBoundingClientRect() が2pxずれる現象への対策 - DenkiYagi