offsetTop/offsetLeft/offsetParentの闇

簡単な定義

要素のoffsetLeftプロパティ
要素の左辺と、offset基準要素の左辺との距離(px)
要素のoffsetTopプロパティ
要素の上辺と、offset基準要素の上辺との距離(px)
要素のoffsetParentプロパティ
要素のoffset基準要素(これが何になるかが問題)

○結論から先に見たい方は>>こちら<<

いままで、なんとなくoffsetTopやoffsetLeftを使っていました^^; これらは「ページ上の要素の位置」を格納してると思って。しかし、よくよく調べてみるとそれは誤りであり、かつかなり複雑でややこしいプロパティであることが分かってきました。*1

もともと、(ここでは述べませんが、offsetHeightとoffsetWidthも含め)offset〜のプロパティはIEがVer5で独自に実装したものらしく、その後他のブラウザが追随して実装したようです。しかし、その実装方法はバラバラになってます・・・。

とりあえず、サンプルを示します。prototype.jsを使用しています。同一ディレクトリにprototype.jsを配置してください(ダウンロードはこちら)。

2006/10/24 追記。更に詳細なサンプルを>>下<<に作成しました。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><!--Quirks mode-->
<!--<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">--><!--Standards mode-->
<html>
<head>
<title></title>
<style>
body{
 /*Opera以外は入れると正しく取れなくなる・・・*/
 /*border:solid blue 2px;*/
}
.mark{
  background-color:red;
  width:50px;
  height:50px;

}
.rel{
  position:relative;
  left:100px;
  top:20px;
}
.abs{
  position:absolute;
}
#mark0{
  left:200px;
  top:20px;
}
#parent2{
  left:400px;
  top:450px;
}
.parent{
  border:solid blue 2px;
  width:200px;
  height:150px;
}
.child{
  background-color:yellow;
  width:160px;
  height:120px;
}
.child_n{
  background-color:yellow;
}
</style>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script>
function test(){
  var ary = [];
  (9).times(function(index){
    ary.push($('mark' + index));
  });
  var str = "<table border><tr><th>id</th><th>offsetLeft</th>"
          + "<th>offsetTop</th><th>offsetParent</th></tr>";
  str += ary.inject("", function(memo, value, index){
    var op = value.offsetParent;
    var id = "";
    if(op == document.body.parentNode){
      id = "[HTML]"
    }else{
      id = op.id;
    }
    memo += "<tr><td>" + value.id + "</td>";
    memo += "<td>" + value.offsetLeft + "</td>";
    memo += "<td>" + value.offsetTop + "</td>";
    memo += "<td>" + id + "</td></tr>";
    return memo;
  });
  str += "</table>";
  Element.update('view', str);
}
function change(obj){
  var ccn = (obj.checked) ? "child" : "child_n";
  var cary = [];
  (3).times(function(index){
    cary.push(new Element.ClassNames('child' + index));
  });
  cary.invoke('set', ccn);
}
</script>
</head>
<body id="body">body
<div id="mark0" class="mark abs">mark0<br>abs</div>
<div id="mark1" class="mark rel">mark1<br>rel</div>
<div id="mark2" class="mark">mark2</div>

<div id="parent0" class="parent">parent0
<div class="child" id="child0">child0
<div id="mark3" class="mark rel">mark3<br>rel</div>
<div id="mark4" class="mark">mark4</div>
</div>
</div>

<div id="parent1" class="parent rel">parent1 rel
<div class="child" id="child1">child1
<div id="mark5" class="mark rel">mark5<br>rel</div>
<div id="mark6" class="mark">mark6</div>
</div>
</div>

<div id="parent2" class="parent abs">parent2 abs
<div class="child" id="child2">child2
<div id="mark7" class="mark rel">mark7<br>rel</div>
<div id="mark8" class="mark">mark8</div>
</div>
</div>

<div style="position:absolute;top:50px;left:400px;">
<button id="test" onclick="test();">TEST</button>
<input type="checkbox" onclick="change(this);" checked/>
<div id="view"></div>
</div>
</body>
</html>

TESTボタンを押すと、要素mark0〜10とtd1のoffsetLeft、offsetTop、offsetParentを表示します。

各要素の主な条件は以下。

ID 条件
mark0 body直下に記述。position:absolute;指定
mark1 body直下に記述。position:relative;指定
mark2 body直下に記述。position指定なし
mark3 要素parent1(position指定なし)の子要素。position:relative;指定
mark4 要素parent1(position指定なし)の子要素。position指定なし
mark5 要素parent2(position:relative;)の子要素。position:relative;指定
mark6 要素parent2(position:relative;)の子要素。position指定なし
mark7 要素parent3(position:absolute;)の子要素。position:relative;指定
mark8 要素parent3(position:absolute;)の子要素。position指定なし

実際にはそれぞれ要素child*が存在します。

基礎知識 position:absolute|relative|static;について(2006/10/16追記)

本題に入る前に、ちょっと確認。

style.positionの値とその効果
static(デフォルト)
通常配置。その要素のstyle.position, left, top以外の要因で決定される位置に配置される。つまりはフツーの配置。
relative
相対配置。上記通常配置からstyle.top, leftの値分ずらして配置する。relative化した要素は、他の要素配置時、元の位置にあるものとして扱われる(元の位置に他の要素が流れ込まない)。
absolute
絶対配置。基準位置からstyle.top, leftの値分ずらして配置する*2。absolute化した要素は他の要素配置に影響しない(元の位置に他の要素が流れ込む)。

ほぼ常識です。ただ、ちょっと気をつけなければいけないのは、position:absolute;時の「基準位置」です。大抵、ページの左上端という認識だと思います。実は、これが少し曲者です。

基準位置は以下のように決まります。

  • ノードツリー上方で直近のposition:absolute;、position:relative;の要素の枠線内側左上端。
  • 以上に該当がなければページの左上端。

これはダブルスタンダードな気がします。前者の「ノードツリー上方の〜」という要素は、後述しますがoffsetParentに同じになるケースが多いです。その要素内の位置なので、top、leftの値が変わらなくても、marginの値が変わると表示位置が変わります。一方、後者のoffsetParentはbody要素になるケースが多いのですが、その場合、body要素のmarginを変更しても表示位置は変わりません。この点は注意が必要です。

2006/12/14 コメントいただいて気が付いたのですが、absolute時(というか全般的に)、(内側の)要素側の基準は要素のmarginを含めた左上端なんですね。枠線外側だと勘違いしてました。

2009/07/29 CSSでの基準要素側の基準を、「コンテンツ」(margin、border-width、padding含まない)としていたのを、「枠線内側」(paddingは含む)に修正しました。

Netscape7.1 Firefox1.5.0.4

素直な動作をするNetscapeFirefoxを最初に見ていきます。

  • 上記例の実行結果
id offsetLeft offsetTop offsetParent
mark0 200 20 body
mark1 108 47 body
mark2 8 77 body
mark3 110 188 body
mark4 10 218 body
mark5 100 58 parent1
mark6 0 88 parent1
mark7 100 58 parent2
mark8 0 88 parent2

mark0〜4まではoffsetParentはbody要素です。

mark0はposition:absolute;です。ここでは示していませんが、absoluteでもoffsetParentは変化します。

mark1はposition:relative;です。offsetLeftが100でなく108なのは、body要素の左マージンのデフォルト値が8だからです。cssでbody{margin:0px;}とすれば100になります。

mark5と6のoffsetParentは要素parent1になります。要素parent1はposition:relative;です。直の親要素であるchild1(position指定なし)は対象になっていません。

mark7と8のoffsetParentは要素parent2になります。要素parent1はposition:absolute;です。直の親要素であるchild2(position指定なし)は対象になっていません。

以上から、offsetParentになる要素の条件は、以下があると考えられます。

  • ノードツリー上方で、position:relative; position:absolute;となる直近の要素。

ここでは触れませんが、実際は更に以下のような条件が追加されます

  • 対象要素がposition指定なしまたはstaticの場合、ノードツリー上方で直近の以下の要素
    • タグ名が「table」「caption」「td」「th」の要素

(この他にも条件がありますが、複雑になるのでここでは省きます。現時点の調査結果は>>こちら<<にまとめています。

  • 以上に該当がなければbody要素。
【参考サイト】
anything from hereHTML要素のサイズや位置を取得する
offsetParentプロパティとは

offsetParentがbody要素で(position:absolute;以外の場合)、offsetLeft、offsetTopにはbody要素のmarginが加算されます。これにより、「ページ上の位置」になるわけです。ただし、border-widthは加算されません。当初、分かりやすいようにとbodyに枠線をつけていたのですが、逆に分かりにくくなるためコメントアウトしました。→2007/07/19追記。 FirefoxはBODYのborder-widthが加算されないどころか、減算されます。。。

Opera9

Operaを見てみます。

  • 上記例の実行結果
id offsetLeft offsetTop offsetParent
mark0 200 20 body
mark1 108 44 body
mark2 8 74 body
mark3 110 178 body
mark4 10 208 body
mark5 102 54 parent1
mark6 2 84 parent1
mark7 102 54 parent2
mark8 2 84 parent2

ほとんどNetscape等と変わりません。ただ、offsetLeftの値を見ると分かるのですが、+2されているケースが多いです。これはborderの2pxです。つまり、OperaはoffsetTop、offsetLeftの基準を要素の枠線内側*3でなく、枠線外側に置いていることになります。 この点は注意が必要です。

どっちがいいかは分からないですが。→累積することを考えると、含んでいるほうがありがたいです^^; 統一してほしいですね・・・。

offsetParentがbody要素で(position:absolute;以外)の場合、offsetLeft、offsetTopにはbody要素のmarginが加算されます。OperaのoffsetLeft、offsetTopはもともとborder-widthを含んでいるので、body要素に枠線があっても正しい値が取れます。

2006/12/12 追記。基準の枠線外/内問題。

これはDIV要素のケースで、Operaは枠線外側、NetscapeFireFoxと後述のIEでは枠線内側が基準になりますが、これがすべてのケース当てはまるわけではないということにさっき気が付きました><; Operaの例外はまだ見当たりませんが、NetscapeFireFoxIEに関して、今のところ以下の例外を見つけています。

  • IEで枠線外側が基準になる親要素
    • TABLE
  • NetscapeFireFoxで枠線外側が基準になる親要素
    • TABLE, TD

たぶん、まだあると思います。おいおい調べていきます・・・。

枠線問題にも対応した座標算出関数を作成しようとしてて見つけたのですが。こうなるとちょっと大変・・・。だからprototype.jsは対応放棄してるのか・・・。

2007/01/22追記。この件に関してはこちらに調査結果をまとめています。

IE6

困ったチャンのIE。本家のくせに一番ややこしい><;

  • 上記例の実行結果(Quirks mode)
id offsetLeft offsetTop offsetParent
mark0 200 20 body
mark1 110 53 body
mark2 10 83 body
mark3 112 191 body
mark4 0 68 child0
mark5 100 56 parent1
mark6 0 68 child1
mark7 100 56 parent2
mark8 0 68 child2

mark0〜3までは問題ないです(bodyのデフォルトマージンが違うくらい)。

問題はmark4。なぜかoffsetParentがchild0。。。

同じく、mark6のoffsetParentがchild1、mark7のoffsetParentがchild2、です・・・。

ここで実験。TESTボタンの横にあるチェックボックスのチェックを外してください。各要素child*の表示が変わります。それから再度TESTボタンを押してみましょう。すると・・・

id offsetLeft offsetTop offsetParent
mark0 200 20 body
mark1 110 53 body
mark2 10 83 body
mark3 112 191 body
mark4 0 86 parent0
mark5 100 56 parent1
mark6 0 86 parent1
mark7 100 56 parent2
mark8 0 86 parent2

素直w→そうともいえませんね^^;(mark4とか)

何をしたかというと、各要素child*のwidth、height指定を外したのです。つまりIEには、offsetParentになる要素の条件について以下があるということです。

  • 対象要素がposition指定なし(またはstatic)のとき、ノードツリー上方で直近のwidthもしくはheightが指定されている要素。

・・・訳がわからん><;

しかし、これでは終わりませんw 次に!DOCTYPE宣言を入れ替えて*4Standards modeにしてリロードし、もう一度TESTボタンを押してみてください。すると・・・

  • 上記例の実行結果(Standards mode)
id offsetLeft offsetTop offsetParent
mark0 200 20 [HTML]
mark1 110 53 [HTML]
mark2 0 68 body
mark3 112 191 [HTML]
mark4 0 68 child0
mark5 100 56 parent1
mark6 0 68 child1
mark7 100 56 parent2
mark8 0 68 child2

ここで、[HTML]というのはHTML要素を意味しています*5IEお得意(?)の「Standards modeではページ上のコンテンツはHTML要素上にあるとみなす」というやつです。・・・だったらなんでmark2のoffsetParentはbodyなのさ><;

つまり、

  • IEのStandards modeでのoffsetParentは、通常body要素のところ、html要素になる。ただし、対象要素がposition無指定もしくはstaticである場合は除く(body要素がoffsetParent)。

・・・><;

いちおう。チェックを外すと。

id offsetLeft offsetTop offsetParent
mark0 200 20 [HTML]
mark1 110 53 [HTML]
mark2 0 68 body
mark3 112 191 [HTML]
mark4 0 86 parent0
mark5 100 56 parent1
mark6 0 86 parent1
mark7 100 56 parent2
mark8 0 86 parent2

この辺調査するのはとても疲れた・・・。

Quirks modeでoffsetParentがbody要素で(position:absolute;以外)の場合、offsetLeft、offsetTopにはbody要素のmarginが加算されます(border-widthは加算されない)。Standards modeではoffsetParentがbody要素の場合、marginは加算されません(div要素等と同じ扱い)。

【まとめ】

上記内容を踏まえ、更に>>下<<の詳細調査用サンプルから導き出した現時点の結論は以下です。。。

2007/01/22追記。例外があります。後述します。

対象要素のoffsetParentになる要素の条件
  • ノードツリー上方で以下のいずれかとなる直近の要素。
    • スタイル指定が「position:relative;」「position:absolute;」の要素。
    • 対象要素がposition指定なし(またはstatic)のとき、タグ名が「table」「caption」「td」「th」の要素。
    • 以下【IEのみ】
      • タグ名が「marquee」「map」の要素。*6
      • 対象要素がposition指定なし(またはstatic)のとき
        • スタイルでwidthもしくはheightが指定されている要素。
        • スタイル指定が「float:left;」「float:right;」の要素。
        • タグ名が「fieldset」「legend」の要素。
  • 以上に該当がなければbody要素。【IE】のStandards modeでは、html要素となる。ただし、対象要素がposition無指定もしくはstaticである場合は除く(body要素になる)。
offsetLeft、offsetTopについての注意点
  • offsetParent要素側の基準は、基本的に、IENetscapeFireFoxで基準要素の枠線内側(左辺、上辺)、Operaで基準要素の枠線外側であるが、例外もある(後述)。これと、対象要素の枠線外側(左辺、上辺)との距離がoffsetLeft、offsetTopになる。
  • offsetParentがbody要素でposition:absolute;以外の場合、offsetLeft、offsetTopにはbody要素のmarginが加算される。ただし【IE】のStandards modeでは加算されない。
    • body要素のborder-widthは加算されない(【Opera】はもともとoffsetLeft、offsetTopがborder-widthを含んでいる)。
    • Firefox】ではbody要素のborder-widthが減算される。

闇はこれで終わらない。。。

とりあえずまとめてみましたが。これで網羅しているのかはちょっと分からず。。。

対応策としては以下があります。

【参考】
function cumulativeOffset(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return [valueL, valueT];
  }

prototype.jsのPosition.cumulativeOffsetメソッドを関数にしただけですが^^; 要素を引数にして、offsetTopとoffsetLeftの累積を配列で返却してます(ただし、Opera以外では、各offsetParentに枠線があるとその分ずれが生じます・・・)。

2007/02/18追記。prototype.jsでの対策を以下に記述しました。

Positionオブジェクトの枠線幅問題対策 - Backstage of theater.js

テストしているうちに気づいたこととして、

Firefox

  • 親要素{position:relative; overflow:scroll; border:solid blue 2px;}
  • 子要素{position:static;}

とすると子要素のoffsetLeftが「-2」(多分、枠線幅×-1)になる。

なんてのもありました。

闇が深すぎます・・・。撤退><;

2007/01/22追記。例外等の調査結果

撤退といいつつ、調査を続けていますが^^;

下の詳細調査用サンプルをよくよく調べてみたところ、「あれ?」というような現象をいくつか見つけたので追記します。上のまとめに追記すると更に訳が分からなくなるので、こちらに書きます。

  • IE
    • offsetParentがTABLE要素の場合、基準は枠線外側。
  • Firefox
    • offsetParentがTABLE, TH, TD要素の場合でposition:static;の場合、すべての要素について基準は枠線外側。
    • position:relative;であるTD要素内の要素について
      • position:absolute;の場合、offsetParentはBODY要素。
      • position:relative;の場合、offsetParentはTD要素で基準は枠線内側。
      • position:static;の場合、offsetParentはTD要素で基準は枠線外側。
    • CAPTION要素内のposition:static;な要素のoffsetParentはTABLE要素となるが、offsetTop/Leftの値はBODY要素を基準にしている。
  • Netscape
    • offsetParentがTABLE, TH, TD要素の場合でposition:static;の場合、すべての要素について基準は枠線外側。
    • position:relative;およびabsolute;であるTD要素内の要素について
      • position:absolute;の場合、offsetParentはBODY要素。
      • position:relative;の場合、offsetParentはTD要素で基準は枠線内側。
      • position:static;の場合、offsetParentはTD要素で基準は枠線外側。
    • CAPTION要素内のposition:static;な要素のoffsetParentはTABLE要素となるが、offsetTop/Leftの値はBODY要素を基準にしている。

・・・結論。TABLE要素内の要素のoffsetParent/Top/Leftは使用しないようにしよう><;

あと、position:absolute;でtop、left無指定時の表示位置が、ブラウザによってかなり異なることも分かりました。。pasition:absolute;時はtop、leftを指定するようにしましょう。(あたりまえか^^;)

FirefoxのBODY枠線問題

http://hkom.blog1.fc2.com/blog-entry-503.html」を読ませていただいて知ったのですが、FirefoxでBODYに枠線があると、その分BODYのoffsetTop、offsetLeftから減算されるんですね・・・。調査の早い段階でBODYの枠線を外してしまっていたので、気が付きませんでした。あとで「Positionオブジェクトの枠線幅問題対策 - Backstage of theater.js」のほうも直しておきます。→直しました。

前述の枠線幅×-1になる件も、何か関連しているのかな・・・。

詳細調査用サンプル

2007/01/22改修。親要素の枠線を2px←→10pxに切り替えるチェックボックスを追加しました。また、いくつか要素を追加しています。

更に詳しく調べるためのサンプル。prototype.js使用です。

TESTボタンを押すと結果テーブルを表示します。テーブルの各idかoffsetParentのセルにマウスカーソルを合わせると、その要素の位置設定情報をツールチップで表示します。

resize childチェックボックスは背景色黄色の全要素のサイズ指定のon/offをします。position abs チェックボックスはposition:absolute;の要素の位置指定をon/offします(チェックを入れると、position:absolute;の要素が一部を除いて画面左上で重なります)。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><!--Quirks mode-->
<!--<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">--><!--Standards mode-->
<html>
<head>
<title></title>
<style>
body{
 /*Opera以外は入れると正しく取れなくなる・・・*/
 /*border:solid blue 2px;*/
 font-size:10px;
 letter-spacing:1px;
}
.mark{
  background-color:red;
  width:30px;
  height:30px;
}
.rel{
  position:relative;
  left:50px;
  top:20px;
}
.abs{
  position:absolute;
  margin:10px;
}
.abs_p{
  left:15px;
  top:20px;
}
.flt{
  float:right;
}
#P-R{
  left:150px;
  top:-10px;
}
#P-A{
  left:200px;
  top:300px;
}
.parent{
  border:solid blue 2px;
  width:120px;
  height:100px;
}
.child{
  background-color:yellow;
}
.child_s{
  width:100px;
  height:80px;
}
#table, #caption, #tr, #th, #td, #td-R, #td-A,#legend, #fieldset, #marquee, #map{
  border:solid blue 2px;
  font-size:10px;
  letter-spacing:1px;
}
#view_t{
  font-size:11px;
  letter-spacing:1px;
  background-color:lemonchiffon;
}
.border{
  border:solid green 10px;
}
</style>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script>
function test(){
  var ary = document.getElementsByClassName("target");
  var str = "<table id='view_t' border><tr><th>id</th><th>offsetLeft</th>"
          + "<th>offsetTop</th><th>offsetParent</th></tr>";
  str += ary.inject("", function(memo, value, index){
    var op = value.offsetParent;
    var id = "";
    if(op == document.body.parentNode){
      id = "[HTML]"
    }else if(op){
      id = op.id;
    }
    memo += "<tr><td";
    memo += " title='position:" + Element.getStyle(value, 'position');
    memo += "; left:" + Element.getStyle(value, 'left');
    memo += "; top:" + Element.getStyle(value, 'top');
    memo += "'>" + value.id + "</td>";
    memo += "<td>" + value.offsetLeft + "</td>";
    memo += "<td>" + value.offsetTop + "</td>";
    memo += "<td";
    if(op){
      memo += " title='position:" + Element.getStyle(op, 'position');
      memo += "; left:" + Element.getStyle(op, 'left');
      memo += "; top:" + Element.getStyle(op, 'top');
    }
    memo += "'>" + id + "</td></tr>";
    return memo;
  });
  str += "</table>";
  Element.update('view', str);
}
function resize(obj){
  var cary = document.getElementsByClassName("child");
  var method = (obj.checked) ? 'addClassName' : 'removeClassName';
  cary.each(function(value){Element[method](value, 'child_s');});
}
function posabs(obj){
  var aary = document.getElementsByClassName("abs");
  var method = (obj.checked) ? 'addClassName' : 'removeClassName';
  aary.each(function(value){Element[method](value, 'abs_p');});
}
function swborder(obj){
  var ary = document.getElementsByClassName("parent");
  ary = ary.concat(['table', 'caption', /*'tr',*/ 'th', 'td', 'legend', 
    /*'fieldset',*/ 'marquee', 'map', 'td-R', 'td-A'].collect(function(value){return $(value);}));
  var width = (obj.checked) ? '10px' : '2px';
  ary.each(function(value){Element.setStyle(value, {'border-width':width});});
}
</script>
</head>
<body id="body">body
<div id="BC" class="child child_s">BC
<div id="M-BA" class="target mark abs">M-BA</div>
<div id="M-BR" class="target mark rel">M-BR</div>
<div id="M-BS" class="target mark">M-BS</div>
</div>

<div id="P-S" class="parent">P-S
<div id="SC" class="child child_s">SC
<div id="M-SA" class="target mark abs">M-SA</div>
<div id="M-SR" class="target mark rel">M-SR</div>
<div id="M-SS" class="target mark">M-SS</div>
</div>
</div>

<div id="P-R" class="parent rel">P-R
<div id="RC" class="child child_s">RC
<div id="M-RA" class="target mark abs">M-RA</div>
<div id="M-RR" class="target mark rel">M-RR</div>
<div id="M-RS" class="target mark">M-RS</div>
</div>
</div>

<div id="P-A" class="parent abs">P-A
<div id="AC" class="child child_s">AC
<div id="M-AA" class="target mark abs">M-AA</div>
<div id="M-AR" class="target mark rel">M-AR</div>
<div id="M-AS" class="target mark">M-AS</div>
</div>
</div>

<div id="P-Fl" class="parent flt">P-Fl
<div id="FlC" class="child child_s">FlC
<div id="M-FlA" class="target mark abs">M-FlA</div>
<div id="M-FlR" class="target mark rel">M-FlR</div>
<div id="M-FlS" class="target mark">M-FlS</div>
</div>
</div>

<table id="table">
<caption id="caption">caption
<div id="CpC" class="child child_s">CpC
<div id="M-CpA" class="target mark abs">M-CpA</div>
<div id="M-CpR" class="target mark rel">M-CpR</div>
<div id="M-CpS" class="target mark">M-CpS</div>
</div>
</caption>
<tr id="tr1" class="target">
<th id="th" class="target">th
<div id="ThC" class="child child_s">ThC
<div id="M-ThA" class="target mark abs">M-ThA</div>
<div id="M-ThR" class="target mark rel">M-ThR</div>
<div id="M-ThS" class="target mark">M-ThS</div>
</div>
</th>
</tr>
<tr id="tr2" class="target">
<td id="td" class="target">td
<div id="TdC" class="child child_s">TdC
<div id="M-TdA" class="target mark abs">M-TdA</div>
<div id="M-TdR" class="target mark rel">M-TdR</div>
<div id="M-TdS" class="target mark">M-TdS</div>
</div>
</td>
</tr>
<tr id="tr3" class="target">
<td id="td-R" class="target rel">td-R
<div id="TdRC" class="child child_s">TdRC
<div id="M-TdRA" class="target mark abs">M-TdRA</div>
<div id="M-TdRR" class="target mark rel">M-TdRR</div>
<div id="M-TdRS" class="target mark">M-TdRS</div>
</div>
</td>
</tr>
<tr id="tr4" class="target">
<td id="td-A" class="target abs">td-A
<div id="TdAC" class="child child_s">TdAC
<div id="M-TdAA" class="target mark abs">M-TdAA</div>
<div id="M-TdAR" class="target mark rel">M-TdAR</div>
<div id="M-TdAS" class="target mark">M-TdAS</div>
</div>
</td>
</tr>
</table>

<div style="height:110px;">&nbsp;</div>

<fieldset id="fiedset">
<legend id="legend">legend
<div id="LgC" class="child child_s">LgC
<div id="M-LgA" class="target mark abs">M-LgA</div>
<div id="M-LgR" class="target mark rel">M-LgR</div>
<div id="M-LgS" class="target mark">M-LgS</div>
</div>
</legend>
fieldset
<div id="FsC" class="child child_s">FsC
<div id="M-FsA" class="target mark abs">M-FsA</div>
<div id="M-FsR" class="target mark rel">M-FsR</div>
<div id="M-FsS" class="target mark">M-FsS</div>
</div>
</fieldset>

<marquee id="marquee">marquee
<div id="MqC" class="child child_s">MqC
<div id="M-MqA" class="target mark abs">M-MqA</div>
<div id="M-MqR" class="target mark rel">M-MqR</div>
<div id="M-MqS" class="target mark">M-MqS</div>
</div>
</marquee>

<map id="map">map
<area id="area" class="target mark">area</area>
</map>

<div style="position:absolute;top:50px;left:400px;">
<button id="test" onclick="test();">TEST</button>
<input type="checkbox" onclick="resize(this);" checked/>resize child
<input type="checkbox" onclick="posabs(this);"/>position abs
<input type="checkbox" onclick="swborder(this);"/>switch border
<div id="view"></div>
</div>
</body>
</html>

*1:offsetの意味は、多分これが一番正解に近いかと→「オフセットとは - IT用語辞典 e-Words

*2:top, leftがnullの場合の扱いはブラウザによって異なります。(以前、デフォルト位置と記述していましたが、誤りです。)

*3:「コンテンツ」(marign、border、paddingを含まない)ではなく、「枠線内側」(paddingは含む)

*4:コメント切替だけでなく、位置も入れ替えてください

*5:offsetParentがdocument.body.parentNodeであるかどうかで判定

*6:Netsape,Firefoxではmap要素内のarea要素にoffsetParentは指定されない