Positionオブジェクト(3)

realOffsetメソッド

【抜粋】
  realOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return [valueL, valueT];
  },

引数の要素と、その親要素すべてのscrollLeftプロパティとscrollTopプロパティの累積値を返却するメソッド。返却値は[(scrollLeftプロパティ累積値), (scrollTopプロパティ累積値)]形式の配列です。累積値にはウィンドウのスクロールも含まれます。

なぜか引数の要素はID指定が不可です・・・。いままでOKだったのに。。。

【例】
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 //EN">
<html>
<head>
<title></title>
<style>
<!--
#scroll1{
  border:solid blue 2px;
  overflow:scroll;
  position:relative;
  width:400px;
  height:400px;
}
#scroll2{
  border:solid blue 2px;
  overflow:scroll;
  position:relative;
  width:200px;
  height:200px;
  margin:90px;
}
#base1{
  width:600px;
  height:600px;
}
#base2{
  width:300px;
  height:300px;
}
#mark{
  background-color:red;
  position:relative;
  top:120px;
  left:120px;
  width:20px;
  height:20px;
}
-->
</style>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script>
<!--
function test(){
  var str = "Position.realOffset($('mark')) = "
          + Object.inspect(Position.realOffset($('mark')));
  Element.update('view', str);
}
//-->
</script>
</head>
<body>
<div id="scroll1">
<div id="base1">
<div id="scroll2">
<div id="base2">
<div id="mark">
MARK
</div>
</div>
</div>
</div>
</div>
<button id="test" onclick="test();">TEST</button>
<div id="view"></div>
</body>
</html>

TESTボタンを押すと要素MARKまでのスクロール量が表示されます。要素やウィンドウのスクロールバーを動かしてからTESTボタンを押すと表示される値が変更されます。

なぜリアルなのかは不明。。。

cumulativeOffsetメソッド

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

引数の要素と、その親要素すべてのoffsetLeftプロパティとoffsetTopプロパティの累積値を返却するメソッド。返却値は[(offsetLeftプロパティ累積値), (offsetTopプロパティ累積値)]形式の配列です。

要素のoffsetParentプロパティは、offsetLeft、offsetTopの基準となる要素を格納しています。通常はbody要素ですが、場合によって変化します。(「何を基準要素(offsetParent)とするか」はかなりややこしい話になります。「offsetTop/offsetLeft/offsetParentの闇 - Backstage of theater.js」を参照してください。)

ブラウザがKonquerorSafariKHTMLの場合、最後に別定義で上書きされます。

枠線幅問題

枠線幅があるとOpera以外で正しく値が取れません(以前の例に枠線があったので修正しました)。
Positionオブジェクト(1) - Backstage of theater.js」の「枠線幅問題」を参照してください。

【例】
<html>
<head>
<title></title>
<style>
<!--
#parent{
  left:200px;
  background-color:yellow;
  position:relative;
  width:200px;
  height:200px;
}
#mark{
  background-color:red;
  position:relative;
  top:100px;
  left:100px;
  width:20px;
  height:20px;
}
-->
</style>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script>
<!--
function test(){
  var str = "Position.cumulativeOffset($('mark')) = "
          + Object.inspect(Position.cumulativeOffset($('mark'))) + "<br/>"
          + "$('mark').offsetLeft/Top = "
          + $('mark').offsetLeft + ", " +  $('mark').offsetTop;
  Element.update('view', str);
}
//-->
</script>
</head>
<body>
<div id="parent">
<div id="mark">
MARK
</div>
</div>
<button id="test" onclick="test();">TEST</button>
<div id="view"></div>
</body>
</html>

TESTボタンを押すと、要素markを引数としたrealOffsetメソッド返却値と、要素markのoffsetLeft、offsetTopプロパティを表示します。 position:relative;の親要素内の子要素の場合、offsetLeft、offsetTopはその親要素を基準にします。

positionedOffsetメソッド

【抜粋】
  positionedOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        p = Element.getStyle(element, 'position');
        if (p == 'relative' || p == 'absolute') break;
      }
    } while (element);
    return [valueL, valueT];
  },

cumulativeOffsetメソッドとの違いは、累積中に、offsetParentプロパティの要素がposition:relativeもしくはabsoluteの場合、そこで累積を中止する点です。position:relative/absolute;の要素の値は加算されません。

【例】
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 //EN">
<html>
<head>
<title></title>
<style>
<!--
#parent{
  position:relative;
  left:200px;
  background-color:yellow;
  position:relative;
  width:400px;
  height:400px;
}
table{
  background-color:blue;
  margin:50px;
}
td{
  background-color:white;
  padding:5px;
}
#base{
  width:300px;
  height:300px;
}
#mark{
  background-color:red;
  width:20px;
  height:20px;
}
-->
</style>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script>
<!--
function test(){
  var str = "Position.positionedOffset($('mark')) = "
          + Object.inspect(Position.positionedOffset($('mark'))) + "<br/>"
          + "$('mark').offsetLeft/Top = "
          + $('mark').offsetLeft + ", " +  $('mark').offsetTop;
  Element.update('view', str);
}
//-->
</script>
</head>
<body>
<div id="parent">
<table><tr><td>
<div id="mark">
MARK
</div>
</td></tr></table>
</div>
<button id="test" onclick="test();">TEST</button>
<div id="view"></div>
</body>
</html>

上記例の実行結果は以下です。

(IE6)
Position.positionedOffset($('mark')) = [57, 57]
$('mark').offsetLeft/Top = 5, 5

td要素は要素markのoffsetParentになります。

枠線幅問題

ここでも枠線幅があるとOpera以外で正しく値が取れません(以前の例に枠線があったので修正しました)。「Positionオブジェクト(1) - Backstage of theater.js」の「枠線幅問題」を参照してください。

offsetTop/offsetLeft/offsetParentの闇・・・

いままで、なんとなくoffsetTopやoffsetLeftを使っていましたが、よくよく調べてみると奥が深い、というより滅茶苦茶です。嫌になるくらい、ブラウザ間で微妙に違います。→調査結果を「offsetTop/offsetLeft/offsetParentの闇 - Backstage of theater.js」にまとめました。