07 | 2017/08 |  09

  1. 無料サーバー

User forum-FC2BLOG-Info-Edit Template-Post-Edit-Upload-LogOut

CSSやJavascript自習の苦闘史を綴っていきたい。恐縮ですがJavascriptを有効にしてご覧ください。
2005年12月から社会問題も掲載!

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

 

offsetLeft,offsetTop,offsetWidthそしてoffsetHeight──静的配置要素の絶対位置を確実に取得する方法について

結論概要

通常配置要素の絶対座標上の位置を取得するJavascriptコードは、ウエブ上や書籍でかなり流布されている。しかし、それらは正しく絶対座標を提供しないものが多い。

だから仕方なく、コードの進行過程をチェックし、ブラウザ毎に必要となる補正を行うコードを追加し、確実に正しい座標値を得る方法を確立した。そのコードはこのエントリイの末尾に掲載し、検討経過を克明に下記にまとめた。

なお、改訂したコードが対応するブラウザはIE7、FF2及びOpera9であり、safariは検証する術がなかった。

object.offset○○は大変便利なプロパティ

それは位置と大きさを与えてくれる。それはマイクロソフトが定義し、Mozzillla系も随随した便利この上ない属性だ。

さて、これらの4つのプロパティは片や位置を、片や大きさを取得する。異なる属性取得を同じoffsetで行うのだ。しかも嫌がらせとしか言いようがないが、<例によって>IEとMozilla系においては、位置属性に係るoffsetLeftとoffsetTopが異なる定義値となっている。

更に、位置とサイズという異なる性格の属性を同一のoffsetなる言葉で包含し、定義してしまったことから、それらは大変混乱しやすい属性となっている。

offsetLeft と offsetTop の定義は?

よく知られているような定義は次のようになっている。(DOM Element リファレンス

offsetHeight
offsetHeightは親要素内で占めている(訳注追加:高さの)ピクセル数を収得します。
offsetLeft
offsetLeftは親要素内での(訳注:オフセット対象要素内での)左からのオフセットのピクセル数を収得・設定します。
offsetParent
offsetParentは当要素がオフセット対象としているオブジェクトの参照を返します(例えば、親要素)。
offsetTop
offsetTopは当要素の親要素からの(訳注:オフセット対象要素からの)上からの相対位置を返します。
offsetWidth
offsetWidthは当要素の親要素での(訳注:オフセット対象要素での)占めている(訳注追加:幅の)ピクセル数を収得します。

しかし、これらの直訳的文章には、margin、border、padding、内容本体のどこまでの位置や幅を指しているのか、全く書かれてない。位置や幅と言えばどこからどこまでを指すのかが重要な要素だが、そのことは当たり前の前提として省略されているか、あるいは余りに複雑なために省略したとしか考えられない訳文である。(原典を調べたら位置の起点と終点については明示されてた。例えばoffsetLeftについては「The distance from this element's left border to its offsetParent's left border.」──つまりborder間の距離である。しかし、border間の距離と言ってもborder自身に幅があるのだから、外辺か内辺かこの文章では分からない。更に幅と高さについては「要素の」としかなく、要素の内側なのか、外側なのかは明示されていない。)

一方、msdnに次のような位置と幅を示す分かりやすい図があり、これを見て初めて上記の文章の具体的な意味が判然とする。(出典はこちら:Welcome to MSDN Library

しかし致命的なことに、これらの説明の一部は間違っている。例えばoffsetLeftが親要素のborder内辺(=padding辺)から当該要素のborder内辺(padding辺)迄となっているが、これは静的要素でも位置指定要素でも共に、IEにおいてさえ間違いであることが後に分かるだろう。

エレメントの位置と大きさ説明図 by Micorosoft

サイズは横方向又は縦方向のborder辺間距離で統一されているが、位置に関しては・・・

よく知られているように、位置を設定する場合、相対配置要素か絶対配置要素かにより起点が変わってくる。CSS上でそれを確認しておく。

前者の場合にはそれが通常配置上あるべき位置からの相対的な移動距離として、、絶対配置の場合には包含する絶対配置ブロックのpadding辺(borderの内側)からの距離としてカウントする。更に、そもそも静的要素の場合にはLeftやTopは定義出来ない。

CSS2.1(仮)- ポジショニングと重ね合わせ http://www.kanzaki.com/book/css/3-3.html から引用
position: static relative absolute fixed
配置別名 (通常配置) 相対配置 絶対配置 固定配置
本章のボックスタイプ名 - 布置ボックス
位置決めスキーム 通常フロー 絶対位置決め
包含ブロック 直近ブロックボックスの内辺 直近布置ボックスのパディング辺 表示域
top、leftなど 適用しない 通常配置からのオフセット 包含ブロックからのオフセット
ブロック整形コンテクスト 形成しない 新たに形成
浮動化 可能 不可

CSS上は上のように静的要素に対するLeftもTopも存在しない。しかし、多くの書籍やサイトに要素の位置を取得するJavascriptが例示されていて、それによればparent要素のoffset値の累積値によって、当該要素のx,y位置を算出している。つまり、CSS上は静的要素のオフセット値は未定義かも知れないが、DOMにおいて、offsetLeftとoffsetHeightが定義されているようだ。しかし、DOMの仕様書をあれこれと検索してみたが、適切な説明は見いだせなかった。例えばDOM Element リファレンスの説明は既に見たとおり、同語反復的説明に過ぎない。

さて、或るエレメントの上下左右に別のエレメントを配置したい場合、以上のようにoffsetLeftやoffsetHeightの定義がはっきり分からないので、ピッタリ配置することは大変難しい。

以下に見るように、IEとFFではこれらの2つの位置属性の仕様が、少なくとも静的要素に関して明らかに異なっているのだ。そこでテストを行って正確な位置取得を行う道を探ることとした。

offsetLeft等に関するテスティング

以下のテストは位置指定されていない通常配置要素について、そのoffset値がブラウザによってどう算出されるのか、試したものである。これらの実験を通じて大変興味深いことが判明した。

結論から言えば、IEとFFにおいて通常配置の位置に関するoffset値は異なる計算をしているということであり、ここでも「ブラウザ別にスクリプトを書かざるを得ない」ことを思い知らされたのである。

テストの方法
  • ブログとは異なるHTML、CSSを適用した殆ど粉飾のないWebページ上において、通常配置のdiv要素を複数配置し、それらを計測対象とした。
  • それらのdiv要素は兄弟関係または子孫関係に配置した。
  • テストに使用したブラウザはsleipnir2.5.13(on IE7)、FF2.0.0.4及びOpera9.21である。
  • 計測に使ったJavascript文は以下のものである。
  • 例5における計測時のsleipnir2のwindowの内幅は983pxであり、この条件下でoffsetLeft値の測定を行った。なおFFとOperaについてもそれぞれ確認したが、このエントリイ内にその結果確認情報は挿入していない。
  • ボックスに関する名称は下図に従った。

    ボックスモデルの寸法説明図

    但し一部日本語化して、margin辺、border辺、padding辺、内容辺と呼ぶこととした。

例1 ───body部のmargin、border、paddingは全てゼロに
offset値測定結果1

上の例1はbody部のmargin、border及びpaddingを全てゼロとした場合の計測値だ。この結果はIEでもFFでも一点を除いて同一であった。異なったのはHeightである。FFの方がIEよりも1pxだけ小さかった。

ブラウザの個性と言って良い、さしたる問題ではないこの相違点はさておき、ここで言えることは位置のoffset値として、対象要素(id:div1)のmargin値が出力されたことである。言い換えればこの場合、bodyから対象要素のborder辺までがオフセット位置ということになる。但し、margin=border=padding=0としたので、body部のどの部分から計測されているのかは、この例では皆目分からない。

例2 ───body部のborderを整数値に(上がIE、下がFireFox)
offset値測定結果2

次の例2は、測定対象(div1)の属性は何も変えず、body部のborderを15pxと変更し、marginとpaddingをゼロとした場合の計測値だ。この結果は早くもIEとFFで異なる様相を呈した。IEはborder幅を加算せず恰もoffset値の起点としてborder内辺=padding辺を採用しているかのように振る舞い、一方のFFは何とbody部のborder幅をマイナスした。この結果FFにおいてはoffset値はLeftもTopもマイナス値となった。

かくして早くもFFのoffsetはそのままでは使いようがないことが暴露された、と言える。

例3 ───body部のmargin及びborderを整数値に(上がIE、下がFireFox)
offset値測定結果3

例3は、測定対象の属性は何も変えずに、body部のborderを15px、marginを13pxとし、paddingをゼロとした場合の計測値だ。この結果混乱はますます加速し、IEとFFで更に異なる様相を呈した。

IEは相変わらずborder幅を加算せず、しかしbodyのmargin値をoffset値に加算して、何とdiv1のmargin+bodyのmarginの値をoffset値とした。borderを飛び越してmargin値を足しているのだ。ここではまた早くも起点としてborder内辺=padding辺を採用している訳ではないことが明かとなった。

一方FFはといえば、今度もまたbody部のborder幅をマイナスしただけではなく、IE同様に、測定対象のbodyに対するoffset値として、測定対象要素のmarginとbodyのmarginを加算している。この結果FFにおいてはoffset値はmargin合計値マイナスbodyのborder値という、何とも理解しがたい幅となった。

上の例3にbody部padding値を適当に与えて計測してみると、位置のoffset値として、IEでは対象要素(id:div1)のmargin値+bodyのmargin値+bodyのpadding値が出力され、FFではこのIEの取得値から何と、body部のborder幅を減じた。こうしてbodyのborder部の扱いが焦点であることが分かる。それにしても加算してないborder値を引いてしまうFFの計測方法は合点がいかない。bodyのborder値を2回引いているとも言えるのであって、これでは何のための計測か分からなくなる。

例4 ───例3の整数値をもっと大きくしてみた(上がIE、下がFireFox)
offset値測定結果4

例4は、以上のことを確認するためにmargin値とborder値を変えてみたものだ。上記の通り、IEはbody部のborderを無視して、2つのmargin値を加算(10+44)してoffset値=54とし、FFはmargin値を加算(54)した上でbody部のborder値をマイナス(-38)して、offset16pxとしている。

以上から通常配置要素のoffset位置の算出方法を、さしあたり次のようにまとめることが出来る。

  • IEもFFも、offset値として、対象要素のmargin値と親要素のmargin値を加算する。
  • IEにおいては親要素のborder値をoffset値の対象としない(無視する)。
  • FFにおいては親要素のborder値をoffset値から除外する(マイナスする)。

孫要素においても上記の結論は適用できるか?

更に確認するために、今度は測定対象を深い階層の孫要素としてみる。推測できることはIEはbodyと途中の各親要素のborder幅を無視するであろうこと、FFはbody部のborderをマイナスするが、途中の各親要素のそれはマイナスしないかもしれない、ということだ。この推測が当たるかどうかお楽しみ!

例5 ───深い階層内にある計測対象で検証
<div id="div5">

margin:8px; border:10px forestgreen solid; padding:6px;background:plum;color:black;line-height:1.1em;

margin:9px; border:11px forestgreen solid; padding:6px;background:plum;color:black;line-height:1.1em;

margin:14px; border:17px springgreen solid; padding:21px;background:powderblue;

margin:6px; border:8px darkgreen solid; padding:6px;background:#dfdfdf;>例7 測定対象要素はこの <div id="div7">この要素は、このエントリイ内におけるdiv要素の四層目に該当する。

さて予測は見事に裏切られた。IEはいくつかのborderを無視し、FFは連なる親要素のmarginもborderもpaddingも全てoffset値にカウントするものの、body部のborderだけはマイナスした。

  • IEでは予想通りbody部のMをカウントし、そのBを無視した。但し途中の親要素のborderは、DOMが親要素としてカウントしたものについてのみ無視している。つまり一部の親要素borderはoffset値にカウントされ、一部のそれは無視された。
  • FFではoffset値をカウントする場合の親要素は仕様上常にbody部しかない。このため結果として途中の親要素のM、B、Pは全てカウントされ、body部のMを加算した上で、body部のBを減算している。
  • 他方、Operaのみが正しく計測した。

結果や如何に?

上記の深い階層にある位置測定要素の計算過程とそれを後述するコードによって補正した値は以下の通りである。これらは皆、ブラウザが表示する過程においてJavascript、関数内で取得した値をdocument.write()で出力した。なおここに過程を説明したブラウザはsleipnir2 onIE7である。

<上記の計算過程出力内容の説明>

「parent-chain」とはこの場での説明上の造語で、offset値算出コードにおいて親要素として把握された要素のリストである。左端は計測対象要素であり、右に順にparentNodeを辿っている。FFとOperaにおいては任意のあらゆる通常要素の親要素は常にBODYであるが、IEの場合には複雑に途中要素のいくつかを親要素とする。

「各offset値」とは、offset値算出コードにおいて、offset値を加算する過程を取りだしたもの。上の数字の意味は下で述べる。IE以外は常に1つの値しかないが、IEは複数の場合がある。

「補正後の正しい位置」とは、無視されたりマイナスされてしまったborder幅をブラウザ毎に適切に加算して得られた、window左辺またはwindow上辺から、対象とする要素のborder辺までの正しいピクセル値である。IEの場合、上の計算においてはoffset値が共に1pxだけ加算されているが、これはコンテナとなっているエントリイ部のborder幅である。他方、FFの場合、BODY部にborder幅を設定してないため、不当に減算されることなく(苦笑)、ここでは、「offsetLeft値=補正後の正しい位置」となり、最後にOperaにおいては、どの様な条件下であっても常にoffsetLeft値=補正後の正しい位置となる。

offset値計算過程を辿って計算内容を解明する(IE限定の記述)

以下はIEの場合に限った内容である。FFやOperaではこのような複雑なことにはならない。

やはりIEでは「こと」は単純ではない。1.body - 2.contaner - 3.column1_block - 4.div - 5.div - 6.div - 7.div(target)という深い階層のターゲットを計測対象にしたのだが、offsetLeft の算出過程を振り返ると、次のようになっている。

まず計測対象とした親要素は、6.5.4.と順番に包含要素を辿るのではなく、いきなり「3.column1_block」を「7.div」のparentNodeとして選択し、これとの距離【Left値:135px】を測っている。

次に3.と2.の距離を測定し【Left値:0px】、最後に2.と1.の距離を測定している【Left値:】。

ここで問題となるのは 3.column1_block に設定されているborderが無視されていることである。勿論body部のBorderも無視されているのだが、このブログのbody部にはmargin:0;border:0;と設定されているので、ここでは問題とならない。

こうしてIEについて言えることは、途中のborderは、計測対象となるものもあれば、そうでないものもある、ということだ。このブログでは時計やカレンダー等の特別なもの以外は配置に当たって「位置指定」を使っていない。特にエントリイ部分は通常配置だけで通している。それでもDOMが親要素として把握するものとそうでない親要素があるわけだ。

そこで、IEの場合にはoffset値計算上の親要素に限って、そのborder幅を無視しないようにコードを修正すれば良いことになる。しかし、ややこしいのは、FFの場合にはIEのような途中の要素のborderを無視するようなことはなく、あくまでも最終の親要素=body要素をoffset値算出対象とする時だけ、そのborderを計算もしてないのに減算するということだ。そして、Operaだけは単純なoffset値の加算だけで正確な絶対位置を知ることが出来る。かくしてブラウザ毎にoffset値を算出(IEとFFは固有の補正が必要)しなければならなくなる。

この時の各ブラウザの計算内容を説明すると下図のようになる。

測定内容説明図

かくして、offset値から正しい絶対位置を知ろうとすれば、IEとFFのそれぞれに対応して適切にborder値を加算する必要がある。それを考慮したJavascriptは以下の通りであり、これはそのままOperaにも適用できる。これで最終的な目的を達したものと確信している。

通常配置要素のoffsetLeftとoffsetHeight値から、window内における当該要素の絶対位置を正確にゲットする方法(IE、FF及びOpera対応。MacPCはないのでsafariは調査できない。)

IE、FF及びOperaにおいて、次のコードによって、対象要素の位置(bodyのmargin辺から対象要素のborder辺までの距離)を算出することが出来る。なお、ここで得られる値は、決してmargin辺やpadding辺までの距離ではないことに留意する必要がある。勿論、もしmargin辺やpadding辺までの距離を知りたければ、得られた値にそれらを減算・加算すれば良いだけだ。一般に絶対配置要素を配置する際にイメージするのはmargin辺やpadding辺の位置ではなくborder辺だと思うので、まずborder辺までの距離を算出することが意味があると思う。

一般に流布しているJavascriptによるoffset値算出方法に変えて、以下のコードを利用すれば、初めて通常配置要素の正確な絶対座標を取得することが出来るだろう。

蛇足

当然のことながら、位置指定された要素(absolute、relative 及び fixed)の位置を、上記の方法で取得してはならないし、する必要もない。元々位置をしているのだから配置する前に作者は位置を知っているのであって、それをscriptで取得するのは余りに馬鹿げている(苦笑)。しかし強いて行うならばobject.style.leftやobject.style.topで簡単に取得できるだろう。

 

● コメント ●

susie-tさんへ:不十分さへのご指摘感謝 (hkom)

 実はこのエントリイを書くに当たり、susie-tさんのサイトを大いに参考にさせていただきました。その旨を全く触れなかったことをここにお詫びします。
 さて、仰るとおり(絶対及び相対)位置指定要素内の通常要素のoffset値がどうなるのかは、全く検討しませんでした。偶々このブログで位置指定要素を部分的にしか利用していないため、その必要性がなかったためです。
 しかし、offset値の有り様について一般化する意義はありますので、(1)位置指定要素内の通常要素、(2)通常要素内の相対位置指定要素内にある通常要素などのoffset値についても検討してみたいと思います。
 貴重なご指摘大変ありがとうございました。引き続きご指導の程お願いします。

● コメント ●

いくつか指摘させてください (susie-t)

はじめまして。susie-tと申します。
私も同じような調査をしておりまして、興味が深く拝見させていただきました。その上で、いくつか気になった点をコメントさせてください。

1.要素自体がstaticでも、親要素がabsoluteやrelativeの場合があります。この場合の対応がなされていないと思われます。具体的には、relativeで枠線つきのDIV内の、staticなDIVの位置を算出する場合、Firefox等で枠線が加算されません。

2.static要素内のrelative要素の位置を算出する場合にも、offestTop、offsetLeftは有用ではないでしょうか。この場合、style.topやstyle.leftは、元の位置からの差分を示すのみで、ページ上の位置を取得するのにはあまり適していないと思われます。

非常に細かい考察をしておられると思います。参考にさせていただき、私のほうの調査結果も見直したいと思っております。こちらにも何か不備な点がありましたらご指摘いただけると幸いです。

■ コメントの投稿 ■

管理者にだけ表示を許可する

●トラックバック●

■トラックバックURLはこちら■
http://hkom.blog1.fc2.com/tb.php/503-ed3350a9

●参照元一覧●

<provided Fc2>
<provided i2i>

▲ToTop

 90%近いシェアを握っているインターネットエクスプローラの描画エンジンを利用したタブbrowser。沢山のタブbrowserがあるが、多機能、カスタマイズフリー、スクリプト利用等で一日の長がある。Gekkoエンジンへの対応も行われ、IEからの自立独立の方向に向かっている。2005年7月にはIE7が登場する見通しの中で、今後の発展が望まれる。

 多様なCSS作成支援機能を備えた、タグ入力式 HTML&CSS作成支援エディタ。スキンデザインもすっきりしている。テキストエディター上で作成するよりも確実で安全にタグ打ちが出来る。
文字コードを選べないのが欠点。

 StyleNote同様のタグ入力式 HTML&CSS 作成支援エディタ。長年使用してきたが現在StyleNoteに乗り換えつつある。

 クリップボード履歴情報を活用する為のソフト。画像まで履歴を取ってくれるのが嬉しい。このソフトを使わない日は絶対ない程に重宝し、愛用している。

 起動中のウィンドウの「コピーできない」説明文などの文字列を取得し、コピー可能な文字データにするツール。何かと便利。

 ストリーミングデータを保存することが出来るソフト。動画利用には不可欠なソフトだ。

 無料ながらレイヤー機能を有し、スクリプトによる拡張も可能な、sleipnir作者が提供している優れもの画像編集ソフト。

 画面キャプチャソフトと言えばこれに勝るものなし、ではないだろうか? 様々な取得方法を有しており、ブログ作成にもHomepage作成に不可欠だ。Jtrimと並んでWoodyBellsの作品。

 複数ファイルの同時編集は出来ないが、透過pngも作れる画像編集ソフト。
(以下当該サイトから抜粋)初心者にも簡単に操作が出来るフォトレタッチソフトです。多くの加工機能で画像に様々な効果を与えることができます。非常に軽快に動作するため、ストレスなく操作できます。

 Animation Gifファイルを作れる無料ソフト。

 キャプチャソフト。画面内にサイト全体が表示しきれない場合でも、これを使えば全体をキャプチャすることが出来る。

 画像処理。画像のフォーマット変換のみならず、色数やサイズ、圧縮率の変更まで一括処理できてしまう『BatchGOO!』は、大量の画像をまとめて処理したいときに大変便利なソフト。BMP, TIFF, JPEG, PCX, PNG の相互変換をはじめ、色数・サイズ・解像度の統一、JPEG圧縮率の調節など、ホームページ用の画像や携帯電話用の壁紙を揃えるのに抜群の相性を見せる。(Vectorの当該ソフト紹介頁より抜粋引用)

 名前から直ぐに想像が付くように画像のサイズを測るためのソフトだ。Homepage作成には欠かせない。2カラム、3カラムのレイアウトを行う場合に大変重宝する。

 ランチャーソフトは沢山あるが、中でもこれが一押しだ。2年以上使ってきたがその操作性には毎日満足している。これを使い始めてからデスクトップには一切のアイコンを表示することをやめてしまった。

 AdobeReader7によって、起動時間が長すぎるという長年のユーザーの不満はある程度解消した。そのためこの高速化ソフトは存在価値が低下してしまったかもしれない。AdobeReader6迄はこのソフトによる起動高速化で恩恵を受けてきた。

 IE専用が難点だが、様々なサイト内でIDやパスワードを入力するのに重宝するソフト。コンテキストメニューから簡単に起動できるのがGood! sleipnir等のIEの描画エンジンを利用しているブラウザでも使える。

 利用しているパソコンの諸元値を取得するには、このソフトがベストだ。インストール済みソフトの一覧が取得できるのも嬉しい。

 WMPは機能が豊富なだけ重い。RealPlayerも同様だ。そこでMedia Player Classicを使いたい。動作が軽快なだけではなく、対応しているファイル形式もすこぶる多く、これひとつで、wmvもrmも表示できてしまうのだから凄い! 数多あるMedia Playerの王様と言えるだろう。

 自宅でPCを起動しているときには必ず起動しているメディアプレーヤー。何かと過剰なWinampよりも、起動も速くスキンはシンプルだ。

 DivX, Xvid, Mov, Vob, Mpeg, Mpeg4, avi, wmv, dv, などの動画をDVD-Video形式に変換できるフリーソフト。クリックするとDVD関連ソフト紹介サイト=「DVDなToolたち」なるHomepageが開きます。

----------
200706220037
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。