ホーム >> 左脳Script >> Java Script >> エレメントの座標取得 ~高速化断念無念orz~

エレメントの座標取得 ~高速化断念無念orz~


マウス座標の件から、なんとなしに、過去の記事のエレメント座標取得のコードを見ると、 この対策がなされたコードがありました。


getPosition =   function(el)
{
    var pos =   el.getBoundingClientRect();
    var html    =   __doc__.documentElement;
    var body    =   __doc__.body;
    return  {   x:(pos.left +   (body.scrollLeft||html.scrollLeft)  -   html.clientLeft)
        ,   y:(pos.top  +   (body.scrollTop||html.scrollTop)    -   html.clientTop) };
};
いかに、今までコードのコピペだけで対処して来ていたのかが露呈してしまったようです。コピペの際は、コードの中身を理解するよう心がけたいものですね。
現実問題、なかなか余裕が無いのですが。


DOCTYPE による場合分け

チョッと気になったので、モード&ブラウザ毎に、どの程度動作に差があるのか調べてみました。

  1. 元祖?!エレメント座標取得ルーチン
    要素のスクロールが考慮されないブラウザが多い。というか、Opera だけ?!
    
    function    getPosition(el)
    {
        var ex  =   0;
        var ey  =   0;
        do
        { 
            ex  +=  el.offsetLeft;
            ey  +=  el.offsetTop;
        }
        while(  el  =   el.offsetParent );
        //
        return  [ex,ey];
    };
    


  2. 素直な取得ルーチン
    スクロール考慮あり。ただし、ループがあるので、Internet Explorer では特に遅くなる。
    
    function    getPosition2(target)
    {
        var ex  =   0;
        var ey  =   0;
        //
        var el  =   target;
        do
        { 
            ex  +=  el.offsetLeft;
            ey  +=  el.offsetTop;
        }
        while(  el  =   el.offsetParent );
        //  要素内スクロール対応
        var el  =   target;
        var bd  =   document.body;
        do
        {
            ex  -=  el.scrollLeft;
            ey  -=  el.scrollTop;
            el  =   el.parentNode;
        }
        while(  el!=bd  );
        //
        return  [ex,ey];
    };
    


  3. getBoundingClientRect使用ルーチン
    ページのスクロール考慮で2種に分ける。

    • TypeA:標準準拠(Standards)モード
      document.documentElement を使っている為、多くのブラウザでこのモードでしか動かない模様。
      
      function    getPosition3a(el)
      {
          var pos =   el.getBoundingClientRect();
          var html    =   document.documentElement;
          return  [   pos.left    +   html.scrollLeft -   html.clientLeft
                  ,   pos.top     +   html.scrollTop  -   html.clientTop  ];
      };
      

    • TypeB:その他のモード
      document.bodyからスクロール量を取得する。
      
      function    getPosition3b(el)
      {
          var pos =   el.getBoundingClientRect();
          var body    =   document.body;
          return  [   pos.left    +   body.scrollLeft
                  ,   pos.top     +   body.scrollTop  ];
      };
      


比較の実際

まず、モードとブラウザで、正しく動作するルーチンを確かめて見ました。マウス座標を表示し、エレメント座標を正しく取得できたルーチンを調査しました。

結果以下のようになりました。枠内に名称のあるブラウザが、対象ルーチンで正しく動作したと判断した物です。
1番目のルーチンは、スクロールの状態を考慮できない為、調査対象から外しました。

各座標取得ルーチンの動作状況
ルーチンDOCTYPE 標準準拠モードDOCTYPE その他互換モード
#2IE6(テーブルの枠幅考慮の必要アリ),IE8,Chrome1,Safari4,Opera9.6,FireFox3
#3.TypeAIE6,IE8,FireFox3,Opera9.6なし
#3.TypeBSafari4IE6,IE8,Safari4,Opera9.6,ForeFox3

  • #2の万能っぷりはすばらしく、殆どのブラウザ、モードによらず座標を得る事が出来るようです。(一部ブラウザのバージョンなどで、ボーダーだかマージンだかのズレが出ましたが。)
  • #3.TypeA、#3.TypeBでは、見事にDOCTYPEモードで動作が分かれます。
  • safari4がモードに関わらず、#3.TypeB でないと正しく動作しないようです。正確には、今回提示したルーチンではダメ(スクロールの補正が逆に余計な処理になっている模様)で、さらに場合分けが必要になります。また、今回調査をしていませんが、Google Chrome 2 も同じような傾向があると思われます。


エレメント座標取得コード

前述の調査結果を踏まえ、状況に応じたgetPositionルーチン(エレメント座標取得ルーチン)を作ってみましょう。

//■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
//  エレメントの絶対座標を得たい
//------------------------------------------------------------------------------
//  座標取得
var getPosition;
var __document__    =   document;

function    is_getBoundingClientRect()
{   //return    (typeof Element.prototype.getBoundingClientRect ==  'function')
    var dummy   =   __document__.createElement("div");
    return  (dummy.getBoundingClientRect)!==undefined;
};

if( is_getBoundingClientRect()  )   //  getBoundingClientRectでの座標取得
{
    //  モード判別:サファリ等は強制的に互換モードのルーチンを使う
    if( __document__.compatMode=='CSS1Compat'   &&  undefined===window.defaultstatus    )
    {   //  DOCTYPE 標準準拠モード
        var __getPosition_base__    =   __document__.documentElement;
        //
        getPosition =   function(el)
        {
            var pos =   el.getBoundingClientRect();
            return  {   x:(pos.left +   __getPosition_base__.scrollLeft -   __getPosition_base__.clientLeft)
                    ,   y:(pos.top  +   __getPosition_base__.scrollTop  -   __getPosition_base__.clientTop) };
        };
    }
    else
    {   //  DOCTYPE その他互換モード
    //  var __getPosition_base__    =   document;
        //
        getPosition =   function(el)
        {
            var pos =   el.getBoundingClientRect();
            var bd  =   __document__.body;
            return  {   x:(pos.left +   bd.scrollLeft)
                    ,   y:(pos.top  +   bd.scrollTop)   };
        };
    }
}
else
{   //  getBoundingClientRectが無い
    if( undefined !== window.opera  )
    {
        getPosition =   function(el)
        {
            var ex  =   0;
            var ey  =   0;
            do
            { 
                ex  +=  el.offsetLeft;
                ey  +=  el.offsetTop;
            }
            while(  el  =   el.offsetParent );
            //
            return  {x:ex,y:ey};
        };
    }
    else
    {
        getPosition =   function(target)
        {
            var ex  =   0;
            var ey  =   0;
            //
            var el  =   target;
            do
            { 
                ex  +=  el.offsetLeft;
                ey  +=  el.offsetTop;
            }
            while(  el  =   el.offsetParent );
            //  要素内スクロール対応
            var el  =   target;
            var bd  =   __document__.body;
            do
            {
                ex  -=  el.scrollLeft;
                ey  -=  el.scrollTop;
                el  =   el.parentNode;
            }
            while(  el!=bd  );
            //
            return  {x:ex,y:ey};
        };
    }
}
//■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

これで、モードやブラウザによらないエレメント絶対座標取得ルーチンが出来た。と思います。
結局、以前に公開したものと、正直たいして代わり映えしない物となってしまいました。


雑感とか

Internet Explorer での遅さを克服するどころか、正しい座標を取得させるのに手一杯なこの状況。
そもそも、何故たかがエレメント座標の取得だけで、こんなにコードを書かなければならないのでしょうか・・・多くの Web製作者共通の苦悩苦悶でしょうね。
こんな現状があるから、Porototype.js や jQuery.js なんかのライブラリが流行らざるを得ない訳で。

今まで、Internet Explorer が強すぎた過去と、W3Cによる規格の策定に時間がかかり過ぎている?のでしょうか。どうも、時代の流れに規格が付いて行けていない気が、個人的にデスガしています。


まぁ、とにかく、正しく座標取得できるレギュレーションの幅がチョッと?広がったので半歩くらい前進です。



トラックバック(0)

トラックバックURL: http://n-yagi.0r2.net/sanoupulurun/mt-tb.cgi/186

コメントする

ホーム >> 左脳Script >> Java Script >> エレメントの座標取得 ~高速化断念無念orz~

アーカイブ

このブログ記事について

このページは、n-yagiが2009年7月 5日 11:27に書いたブログ記事です。

ひとつ前のブログ記事は「クラス内で唯一の共通プロパティ」です。

次のブログ記事は「任天堂のゲームのスキップについて」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

Creative Commons License
このブログはクリエイティブ・コモンズでライセンスされています。