m2O

2007/04/02 (月)

Opera の scrollTop のバグ

|  Opera の scrollTop のバグ - m2O を含むブックマーク

こんな話は有名で、誰でも知ってることなのかどうかわからないんですが、Opera9 の scrollTop にはバグ*1があります。

(function(){
  var dv=document.createElement('div');
  dv.innerHTML='<span>a</span>';
  document.body.appendChild(dv);
  alert(dv.scrollTop);            // これは 0
  alert(dv.firstChild.scrollTop); // 0 でない!!
  alert(dv.offsetTop);            // この値と同じ
})()

inline 要素なのに scrollTop に値を返してしまうんですね。
僕はこのバグYahoo! UI Library (YUI)ソースを読んで初めて知りました。

--

2007/04/05 追記
そういえば textarea のスクロール量を取ろうとして scrollTop を参照したら妙な値だったのを思い出しました。結局諦めたのですが Opera で textarea のスクロール量はどうあがいても取得できないんですかね?

--

YUI(2.2.0a) では getXY(要素の絶対位置を返す) という関数の中で、このバグを以下のように回避しています。

if (el.parentNode) { parentNode = el.parentNode; }
else { parentNode = null; }
while (parentNode && parentNode.tagName.toUpperCase() != 'BODY' && parentNode.tagName.toUpperCase() != 'HTML') 
{ // account for any scrolled ancestors
    if (Y.Dom.getStyle(parentNode, 'display') != 'inline') { // work around opera inline scrollLeft/Top bug
        pos[0] -= parentNode.scrollLeft;
        pos[1] -= parentNode.scrollTop;
    }
    
    if (parentNode.parentNode) {
        parentNode = parentNode.parentNode; 
    } else { parentNode = null; }
}
Yahoo! UI Library (YUI) - dom.js

この部分は「overflow:scroll」な要素内の要素の絶対位置を取得するための処理で、親要素が「display:inline」の場合はスクロール分の減算を行わないようにしています。

が、困ったことに Opera のこのバグは inline 要素だけではないんです。

(function(){
  var dv=document.createElement('div');
  dv.innerHTML='<table><tbody><tr><td>a<tr><td>b</tbody></table>';
  document.body.appendChild(dv);
  alert(dv.firstChild.firstChild.firstChild.scrollTop); // 最初の tr // 2
  alert(dv.firstChild.firstChild.lastChild.scrollTop);  // 最後の tr // 25
})()

tr の scrollTop はどんどん加算されてく感じです。
これだけだと table のネストが無ければ影響なさそうですが、他にも

(function(){
  var dv=document.createElement('div');
  dv.innerHTML='<table><thead><tr><td>aaa</thead><tbody><tr><td>aaa</tbody></table>';
  document.body.appendChild(dv);
  alert(dv.firstChild.firstChild.scrollTop); // thead の scrollTop // 0
  alert(dv.firstChild.lastChild.scrollTop);  // tbody の scrollTop // 23
})()

こんなのがあって、table 周辺はもうダメダメっぽいです。

YUIバグレポートしようかと思ったら既にあがってました。

むしろOperaが直すべきだけど。

*1:「inline要素のscrollTop(Left)の値は未定義」ということであればバグではなく仕様ということになりますね。

トラックバック - http://orera.g.hatena.ne.jp/miya2000/20070402