User forum-FC2BLOG-Info-Edit Template-Post-Edit-Upload-LogOut
1 月 21 日、ついにそれが出た! 日本におけるスマートフォンの本格的普及=「スマートフォン戦争」の火ぶたが切られたのだ。
iPhone が一人勝ちした 2009 年は、日本の「スマートフォン元年」と言って良いだろう。
そして、一人勝ち故の競争の欠如が、Apple と ソフトバンクを奢らせていることは間違いない。
その意味で、競争が始まるとすればユーザーから見れば歓迎すべきことに違いない。
iPhone キラーは iPhoneか、Xperiaか?--ドコモ「Xperia」ファーストインプレッション:レビュー - CNET Japan に紹介されている。
iPhone との大きさやデザイン比較は、iPhoneキラー『ドコモ スマートフォン Xperia SO-01B(Xperia X10)』業界最速動画レポート! に掲載されている写真が参考になるし、同サイトからアクセス出来る you tubeの各種動画も商品理解を深める。例えばこれなど。
注目の料金は、”パケ・ホーダイ ダブル”のスマートフォン定額通信料で月額 5,985 円だそうだから、割引なしの iPhone 定額通信料よりも約千円安い。
そしてカメラ解像度は日本では非常識な iPhone の 300M に比べて 810M と十分だし、何よりも落としやすいサイズなのにストラップ穴のない iPhone がデザイン偏重の自己陶酔主義であるのに対して、Xperia はしっかりストラップが付けられるのも素晴らしい。
要するに、Xperia は日本の実情を踏まえた「KAIZEN」が施されていると言えるだろう。
そしてデザインなども重要な要素だが、何よりも重要なことは競争によって定額通信料が少しでも安くなることだ。それこそが、iPhone の伸びを<天文学的水準>にまで到達させなかった原因であり、第 2 のスマートフォン Xperia も初期価格及びランニングコストで魅力がなければ、高嶺の花とならざるを得ない。
何よりも通信料の値下げを強く、強く望みたい。
先行した Mac を Windows が追いかけ、追い抜いたように、Xperia は iPhone を凌駕するのだろうか?
否、一世を風靡したウォークマンが iPod によって、僅か 1 年でシェアを奪われてしまった恨みを晴らすように、Xperia ( Sony Ericsson 製 )が iPhone の牙城を崩し、トップシェアを奪うのだろうか?
それとも、iPhone が引き続き独走し続けるのだろうか?
野次馬的興味は尽きない。
確か 1 月 14 日が jquery.js の誕生日なのだそうだ。
そして、毎年、その時が来ると確実にバージョンアップが重ねられてきた。
今年も同様に、1.3.2 から 1.4 へとバージョンアップされ、非圧縮ファイルでは 何と 6000 行を数えるまでに巨大化した。
さて、現在進行中の「 HTML 要素の位置・サイズ取得に関わるメソッド等の解読 」作業に当たって、1.3.2 でほぼ作業を終えていたのだが、折角バージョンアップされたのだからと、大変なルーティンワークだったが、「 HTML 要素の位置・サイズ取得に関わるメソッド等の解読 」に係るエントリイ(エントリイNo.749 ~753)を、全て 最新版の 1.4 対応に改訂した。
その改訂作業を行いながら気になったのだが、今回のバージョンアップでは、かなり堅実な記法が採用されていることだ。if 条件 を適用する行が 1 行しかなくても(必要ないのに) { } で括ったり、余り必要ないと思われる箇所でも this[0] をわざわざ elem 変数に代入したり、不要と思われる過剰な記述が目立つのだ。
これはおそらく、バグチェックしやすいように、確実な記法を採用した結果と思われるが、クールな記法が特徴だった jquery.js だけに、何故か寂しい気がする。
しかも、jquery.js は年々巨大化している。致し方ないこととは言え、今後も止まることなく巨大化を続けていくのだろうか?
機能の豊富さをキャッチアップしていくこと自体、大変な手間であり、そのことが利用のハードルを高くするのではないか、と懸念する。
例えば、簡易版のようなものを検討する必要があるのではなかろうか、とさえ思えてくる。まあ、そのようなものとして使える core.js があるが...
それでも食らいついて、キャッチアップしようと必死になっているのは、jquery.js が javascript 学習の「教科書的」存在と言えるからである。
その存在を知り、大胆不敵にも解読しようと決意したのは、2007 年 10 月だった。
そして、その存在を知るまでと、2 年余の学習を重ねてきた現在とを比べれば、Javascript に関する知識とその利用度合いは、幼稚園児と大学生程の天地の差が付いている。我ながら嬉しい驚きを覚えるほどだ。
そのような進化を遂げることが出来たのは、偏に jquery.js に食らいき、それを何とか理解してやろう、そして理解した上で jquery.js を利用して縦横無尽にコードを書いてみたい、という単純であるが、強い欲求の結果に他ならない。
今後もますます巨大化し、多機能化することは間違いないだろうが、引き続き食らいついて行こう、と決意を新たにしている昨今である。
これらの 6 つのインスタンスメソッドは、HTML 要素の大きさを計測するか、あるいは大きさを指定するメソッドです。(サイズ指定は、height 及び width メソッドのみ。つまり content だけがサイズ指定できる。)
まず、以上のメソッドを分かりやすく図解してみました。横幅の説明だけですが、敢えて煩雑にしてまで縦方向を示す必要はないでしょう。
これらのメソッドが、W3C 勧告の視覚整形モデルに準拠したボックスの各部寸法を取得するためのものであることは、図から一目瞭然です。
これらのメソッドは 1 つの jQuery.each クラスメソッド内で定義されています。
このクラスメソッド内では、三項演算子が頻繁かつ多重的に使用されており、全体で 50 行近い長いコードとなっています。
これらのメソッドは html 要素に適用できるので、ページ内の各要素のサイズ計測に利用できることは勿論ですが、window、body、document などの頁等の全体を指す要素に対して適用して、文書全体の高さや window の横幅などの情報を得ることも出来ます。
5946:// Create innerHeight, innerWidth, outerHeight and outerWidth methods // 'Height' と 'Width' を対象として巡回走査する 5947:jQuery.each([ "Height", "Width" ], function(i, name){ 5948: // i が 1 の時には変数 tl に Leftを、i が 0 の時には Top を代入する。 5949: var type = name.toLowerCase(); 5950: 5951: // innerHeight and innerWidth // jQuery インスタンスメソッド innerHeight 及び innerWidth を定義する。 5952: jQuery.fn["inner" + name] = function(){ 5953: return this[0] ? // インスタンスに HTML 要素が登録されていれば // 最初の HTML 要素の padding 辺までの height 又は width プロパティ値を取得する。 // false 指定があるので算出スタイル値ではなく指定済みの CSS 値が取得される。 5954: jQuery.css( this[0], lower, false, "padding" ) : 5955: null; // インスタンスに HTML 要素が登録されていなければ null 値を返す。 5956: }; 5957: 5958: // outerHeight and outerWidth // jQuery インスタンスメソッド outerHeight 及び outerWidth を定義する。 5959: jQuery.fn["outer" + name] = function(margin) { 5960: return this[0] ? // インスタンスに HTML 要素が登録されていれば /* 引数が true ならばその margin 辺までの、引数が * 無指定か false ならばその border 辺までの、height 又は width CSS * スタイル値を取得する。ここでも false 指定があるので、算出スタイル値 * ではなく指定されている CSS スタイル値が取得される。 */ 5961: jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) : 5962: null; // インスタンスに HTML 要素が登録されていなければ null 値を返す。 5963: }; 5964: // jQuery インスタンスメソッド width() 及び height() を引数 size 付きで // jQuery.prototype オブジェクトに登録する。 5965: jQuery.fn[ type ] = function( size ) { 5966: // Get window width or height 5967: var elem = this[0]; // インスタンスに登録されている最初の html 要素への参照 5968: if ( !elem ) { // elem がない場合の処理である // 引数 size がなければ nullを返し、あれば jQuery インスタンスを返す。 5969: return size == null ? null : this; 5970: } 5971: // <ケース 1> elem が window の時には、 5972: return ("scrollTo" in elem && elem.document) ? // does it walk and quack like a window ? // ↑ は慣用句なのでしょうか?意味が分かりません。 5973: // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode // 人は皆は、標準/互換モード判別に document.documentElement か document.bodyを使用する。 // CSS1標準モードならば document.documentElement.clientHeight/clientWidth を 5974: elem.document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] || // 互換モードならば document.body.clientHeight/clientWidth を返す。 5975: document.body[ "client" + name ] : 5976: 5977: // Get document width or height // elem が document の場合 5978: (elem.nodeType === 9) ? // is it a document 5979: // Either scroll[Width/Height] or offset[Width/Height], whichever is greater // scrollWidth/Height 値と offsetWidth/Height 値の大きい方を返す。 5980: Math.max( 5981: elem.document.documentElement["client" + name], 5982: elem.body["scroll" + name], elem.document.documentElement["scroll" + name], 5983: elem.body["offset" + name], elem.document.documentElement["offset" + name] 5984: ) : 5985: 5986: // Get or set width or height on the element // <ケース 2> elem が window でない場合 5987: size === undefined ? // <ケース 2-a>引数 size が未定義の時には 5988: // Get width or height on the element // elem の height 値 または width 値を px 単位付きで返す。 5989: jQuery.css( elem, type ) : 5980: 5991: // Set the width or height on the element (default to pixels if value is unitless) /* <ケース 2-b>引数 size がある場合には * インスタンスに登録されている HTML 要素の height 又は width 値を * 引数が文字列型ならば size とし、引数が文字列型でないならば * size に px 文字列を追加した値とする。 */ 5992: this.css( type, typeof size === "string" ? size : size + "px" ); 5993: }; // 5965行の function はここまで 5994: 5995:});
これらのメソッドは引数を 1 つ取ることが出来、引数 val がある場合には指定した値だけ、window または対象要素を横または縦にスクロールさせます。
引数がなければ window または対象要素の、横または縦のスクロール量(ピクセル値、単位はなし)を取得します。
返値は val がある場合には jQuery インスタンスオブジェクトであり、val がない場合には発生しているスクロールのピクセル数です。
ここに window または対象要素にスクロールバーがない場合、引数 val にゼロでない値を指定しても当然対象要素にスクロールは発生せず、インスタンスオブジェクトが返されます。一方、引数 val がない場合にはゼロが返されます。
なお、このメソッド定義では jQuery.each クラスメソッド内に、jQuery(xxx).each インスタンスメソッドが入れ子になっており、かつ this が多用されています。その意味では、いかにも jquery.js ならではの、興味深いコードとなっています。
■jQuery().scrollTop()/scrollLeft() メソッドの解読 5901:// Create scrollLeft and scrollTop methods // 'Left' と 'Top' を対象として巡回走査 5902:jQuery.each( ['Left', 'Top'], function(i, name) { 5903: var method = 'scroll' + name; // 変数 method に 'scrollTop'、'scrollLeft'を順に代入 5904: // jQuery.prototype オブジェクトに scrollTop と scrollLeft メソッドを追加する 5905: jQuery.fn[ method ] = function(val) { 5906: var elem = this[0], win; // ローカル変数定義 5907: // jQuery インスタンスに登録された HTML 要素がなければ何もしないで終わる 5908: if ( !elem ) { 5909: return null; 5910: } 5911: 5912: if ( val !== undefined ) { // <ケース1> 何らかの val が与えられている場合 5913: // Set the scroll offset スクロール値をセットする // 'scrollTop' と 'scrollLeft'を巡回処理する。なお、この行の this は // elem をプロパティとして持つ jQuery インスタンスを参照する。 5914: return this.each(function() { 5915: win = getWindow( this ); // 関数起動 5916: // <ケース1-1> 登録要素が window ならば 5917: if ( win ) { 5918: win.scrollTo( // window.scrollTo メソッドを起動する /* win.scrollTo(val,jQuery(win).scrollTop())、 * 又は win.scrollTo(jQuery(win).scrollLeft(),val) * を実行する。 */ 5919: !i ? val : jQuery(win).scrollLeft(), 5920: i ? val : jQuery(win).scrollTop() 5921: }): 5922: 5923: } else { // 登録要素が登録要素が window ではない場合 5924: this[mehod] = val; // 対象要素内でスクロールする。 5925: } 5926: }); 5927: } else { // <ケース2> val が未定義の場合 5928: win = getWindow( elem ); 5929: 5930: // Return the scroll offset /* <ケース2-1> 登録要素が window の場合 * win に pageXOffset プロパティが存在する場合には、 * i が 1 ならば win.pageYoffset を、i が 0 ならば win.pageXoffset を返す。 */ 5931: return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] : // jQuery.boxModel が true ならば document.documentElement[ method ] // プロパティ値を取得する。(これは IE の標準モードの場合に該当する) 5932: jQuery.boxModel && document.documentElement[ method ] || // さもなければ document.body[ method ] プロパティ値を取得する。 //(これは IE の互換モードの場合に該当する) 5933: win.document.body[ method ] : // <ケース2-2> 登録要素が window でないならば、 // 登録要素のスクロール値を取得して返す。 5934: elem[ method ]; 5835: } 5836: }; 5837:}); 5938: 5939:function getWindow( elem ) { // 関数定義 // elem に scrooTo メソッドが適用でき、かつ elem に document がある場合 // つまり、elem が window の場合 5940: return ("scrollTo" in elem && elem.document) ? 5941: elem : // elem を返す。 5942: elem.nodeType === 9 ? // そうでない場合には elem が html 要素ならば // elem の defaulView プロパティ値、又は elemの parentWindow プロパティ値を返す。 5943: elem.defaultView || elem.parentWindow : 5944: false; // elem が html要素でなければ false を返す。 5945:}
これら 2 つの jQuery インスタンスメソッドは、共に読み取り専用で、返値は共に座標オブジェクトですが、座標系と計測箇所が異なります。
offset メソッドがドキュメント座標における対象要素のボーダー辺の座標値を取得するのに対して、position メソッドは、対象要素の margin 辺の、基準要素( offsetParent )内における座標値を取得します。つま、offset メソッドによる座標値は対象要素の margin 値を含むのに対して、position メソッドによる座標値は margin 値を含みません。
しかし、position メソッドは offset メソッドを利用して基準要素から margin 辺までの offset 座標値を算出します。
5857:jQuery.fn.extend({ // jQuery protptype オブジェクトに position メソッドを 5858: position: function() { // インスタンスメソッドとして登録する 5859: if ( !this[0] ) {// インスタンスに要素ノードが 1 つも登録されていなければ 5860: return null; 5861: } 5862; 5863: var elem = this[0]; // ローカル変数定義開始 5864: 5865: // Get *real* offsetParent // 独自メソッドを使って真の offsetParent 要素を取得して、 // offsetParent 変数に代入する 5866: var offsetParent = this.offsetParent(), 5867: 5868: // Get correct offsets // offset ローカル変数に計測対象要素の offset メソッドの返値を代入 5869: offset = this.offset(), /* offsetParent が body または html ならば、ローカル変数 parentOffset に * { top: 0, left: 0 }オブジェクトへの参照を代入し、offsetParent が別の要素 * ならば offsetParent.offset() の返値オブジェクトへの参照を代入する。 */ 5870: parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset(); 5871: 5872: // Subtract element margins マージン値を減算する 5873: // note: when an element has margin: auto the offsetLeft and marginLeft 5874: // are the same in Safari causing offset.left to incorrectly be 0 // 注記:「要素に margin : auto が設定されている場合、 // safari では、offset.left が不正にゼロとなってしまうために、 // offsetLeft 値 と marginLeft 値が同一になってしまう。」 /* num メソッドは jquery.js の無名関数のトップレベルで定義されている関数 * (#1266~1268)。第 1 引数で指定した HTML 要素の、第 2 引数で指定した算出 CSS * スタイル値(単位は含まない)を取得するもの。 * 描画された状態から marginTop/Left 値を取得して、これを offset.top/left 値から * 減算する。offset() メソッドは border 辺までの値を保持しているので、この減算に * よって offset.top/left は margin 辺までの値となる。 */ 5875: offset.top -= num( this, 'marginTop' ); 5876: offset.left -= num( this, 'marginLeft' ); 5877: 5878: // Add offsetParent borders // 上のコメントの通り、offsetParent の座標値に border 幅を加算する。 5879: parentOffset.top += parseFloat( jQuery.curCSS(offsetParent[0], "borderTopWidth", true) ) || 0; 5880: parentOffset.left += parseFloat( jQuery.curCSS(offsetParent[0], "borderLeftWidth", true) ) || 0; 5881: 5882: // Subtract the two offsets // 2 つの offset 値を減算して、基準要素( offsetParent ) のパディング辺から // 対象要素の margin 辺までの距離を算出する。 5883: return { 5884: top: offset.top - parentOffset.top, 5885: left: offset.left - parentOffset.left 5886: }; 5887: },
#5865 にあるとおり、このメソッドは「Get *real* offsetParent」のために作成されました。
■jQuery().offsetParent() メソッド解読 5889: offsetParent: function() { // map メソッドの返値を返す。 5890: return this.map(function() { 5891: var offsetParent = this.offsetParent || document.body; // offsetParent が存在して、タグ名が body でも html でもなく、 // position 値が static である限り繰り返す。 5892: while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') ){ // 1 つ上位の offsetParent への参照を変数 offsetParent に代入する。 5893: offsetParent = offsetParent.offsetParent; 5894: } 5895: return offsetParent; // offsetParent 要素への参照を返す。 5896: }); 4296: } 4297:});
解読対象コードは以下のメソッド等とし、この順に解読を行います。なお対象とする jquery.js はバージョンアップされたばかりの ver 1.4 とします。
このオブジェクトには 3 つのメソッドが定義されます。
initialize は、ブラウザ特性を把握するために幾つかのプロパティを設定するメソッドです。
この「幾つかのプロパティ」とは次に列挙した 5 つで、これらは、jQuery().offset() メソッド解読において後述するように、要素の正確な位置算出を行うために使われ、それは同時に、各ブラウザの CSS に関する特性を捉えることになります。
上の 5 つのプロパティは、ブラウザのそれぞれの仕様を個別具体的に把握するために用意されたもので、jQuery.offset オブジェクトの 1 つの目的は、この 5 プロパティチェックにあると言えるでしょう。これらのプロパティによってブラウザ毎のバグや仕様の差異がチェックされ、位置算出で補正が必要かどうか判断されるのです。
jQuery.offset オブジェクト内で定義される 2 つめのメソッド jQuery.offset.bodyOffset は、document.body の margin 値を top/left 値に追加し、その値をオブジェクトとして返します。返値の top/left 値は document.body のボーダー辺のドキュメント座標値を示すはずです。しかし、Firefox では body 部に border がある場合に、奇妙な offsetTop/Left 値を返すので、正確な座標値にならない場合があります。(このことの詳細は後述します。)
jQuery.offset オブジェクト内で定義される 3 つめのメソッド jQuery.offset.setOffset は、jquery.js ver1.4 で新設されたものです。
これまでの jQuery(elems).offset インスタンスメソッドは読み取り専用でしたが、ver 1.4 では、elems HTML要素(静的配置の場合も含む)を、配置換えする引数を取ることができるようになりました。この引数は配置換え先となる top、left プロパティを持つオブジェクト(仮に obj と呼ぶ)か、elems のインデックス番号 i と、i 毎の obj の 2 つの引数をもつ関数か、そのいずれかとなります。
前のエントリイで触れた W3C Working Draft 04 August 2009 - CSSOM View Module における offsetTop/Left プロパティ定義案を標準と考えた場合、jQuery.offset オブジェクトの 5 つのプロパティが、ブラウザ(全て windows 版)毎にどのようになるのかを一覧にしてみました。
ここに、doesNotSubtractBorderInBodyOffset は私が独自に作成したプロパティです。document.body にゼロではない border 幅が設定されている場合に、document.body.offsetTop 値から、このボーダー値が減じられてしまうかどうかを調べるために設けたものです。
W3C の列は W3C Working Draft 04 August 2009 - CSSOM View Module での提唱案に従った場合の各プロパティ値です。これを標準と考えて各ブラウザを比較してみます。
すると、標準モードで border なしの場合、Opera と IE8 では doesNotAddBorder が false となることにおいて、Chrome では doesAddBorderForTableAndCells が false となることにおいて、最後に Firefox では doesNotSubtractBorderInBodyOffset が false となることにおいて、それぞれ標準とは異なっています。
つまり、W3C 草案に全て一致するブラウザは現在存在しません。
supportsFixedPosition は、position:fixed をサポートしない IE 及び Opera の後方互換モードでは false となります。
Firefox では document.body に ゼロ幅ではない border がある時とない場合とで、offsetTop/Left が異なる値を示します。border があると offsetTop/Left 値は「マイナスボーダー幅」となるのです。標準でも互換でもいずれのモードでも同様です。
そこで、doesNotIncludeMarginInBodyOffset プロパティを作って調査しました。
こうして、Firefox だけが border 有無によって offsetTop に異なった値を返すことが判明しましたが、これは明らかにバグというべきでしょう。
IE8 の場合、doesNotAddBorder は標準モードと互換モードで異なる値になります。これは IE 互換モードの content.width/height が W3C 標準と異なって border 辺までの値として定義されていることに関連していると推測されます。
W3C CSS2.1 の仕様として、position : absolute と position : relative では配置原点が異なります。前者は offsetParent のパディング辺が基準となり、後者は offsetParent の内容辺が基準になります。そして内包されるボックスの margin 辺がそれぞれの配置原点に配置されます。(下図参照)
subtractsBorderForOverflowNotVisible プロパティは、或る要素 B の absolute 属性を一時的に relative に変更すると共に、B の overflow 属性を hidden にして、包含要素 C の padding 値だけ当該要素を右下に移動させた時に、当該要素内に静的に配置された子要素 A も B と一緒に動くかどうかを調べています。
上述の「 jQuery.offset オブジェクトの 5 つのプロパティ 」 に掲載した表にあるように、Windows 系の主要ブラウザにおいては、標準モードであれ互換モードであれ、subtractsBorderForOverflowNotVisible プロパティは全て false となります。ですから、どんなブラウザで、このプロパティ値が true となるのか分かりません。つまりこのプロパティの必要性が判然としません。
因みに、その他の jQuery.offset オブジェクトのプロパティでは、私が独自に設定した doesNotSubtractBorderInBodyOffset も含めて、いずれかのブラウザの、いずれかのモードにおいて、他と異なる値となるので、これらのプロパティはブラウザ特性を把握するために必要であることが分かるのですが、subtractsBorderForOverflowNotVisible プロパティだけは、必要性が分かりません。
このプロパティは、Mac 系のブラウザや Windows 系の他のブラウザにおいて、必要なのかもしれませんが、チェックし得ないため不明です。
■jQuery.offset オブジェクト // jQuery.offset オブジェクトの定義を開始する 5785:jQuery.offset = { // initialize メソッド定義を開始する。引数はない。 5786: initialize: function() { // ローカル変数定義 /* 変数 html にはブラウザ特性をチェックするための 2 つの要素をセットしている。 * 1 つは 4 辺共黒で塗りつぶした 5 px 幅のボーダーを持ち、コンテンツ幅も * 高さも 1 px の div タグ内に設けるチェック対象の空の div タグ。 * そして 4 辺共黒で塗りつぶした 5 px 幅のボーダーを持ち、幅と高さを 1 px、 * セル余白もセル間隔もゼロの table タグ内に配置するチェック対象の空 td タグ。 * これらの 2 つの要素を document.body の左上隅にそれぞれ絶対配置して、 * offsetTop プロパティを使ってブラウザ特性を測定する。 */ 5787: var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0, 5788: html = '
続いて、2 つめのコード解読を行います。
このメソッドの目的は、知りたい HTML 要素の border 辺のドキュメント座標値 top 及びleft を知ることです。そのために上述のように、jQuery.offset.bodyOffset オブジェクトの top/left プロパティに、body 部の margin 値を加算して border 辺迄の座標を得ています。(#4248~4255)
この結果、body 要素自体に border(幅 W )がある場合には、body.offsetTop = 0 (但し、firefox のみ、body.offsetTop = - W となってしまう)ですが、$("document.body").offset().top = marginTop 値 となります(ここでも firefox の場合のみ、marginTop - W となってしまいます)。
このメソッドでは CSSOM で定義されようとしており、現時点では何ら標準化されていない 1 つのメソッド( getBoundingClientRect )を使って、あるいは同様に標準化されていない 3 つのプロパティ( offsetParent、offsetTop 及び offsetLeft )を使って、要素のドキュメント座標を算出します。
A.getBoundingClientRect メソッドをサポートしているブラウザにおいては、要素 A のクライアント座標値が取得でき、その値はブラウザ間で差異がないので、容易にドキュメント座標値が取得できます。A.getBoundingClientRect メソッドで取得したクライアント座標値に window スクロール値を加算し、clientTop/Left値を減算すれば、要素 A の正確なドキュメント座標値が取得できます。
一方、ブラウザが getBoundingClientRect メソッドをサポートしていない場合には、offsetParent、offsetTop 及び offsetLeft を利用してドキュメント座標を取得するのですが、これらのプロパティはブラウザ毎に解釈が異なっていたり、バグがあったりするので、そのままでは正しい位置が取得出来ません。そこで、上で解読した jQuery.offset オブジェクトの 5 つのプロパティを駆使して、ブラウザ毎・標準互換別の offsetTop プロパティ算出値の差異を補正し、jQuery.offsetParent メソッドによって、ブラウザ間で定義が異なる offsetParent の一意的な確定を行います。
こうして要素のドキュメント座標値( border 辺迄の値 )をブラウザに依存せずに正確に算出する訳です。
jQuery().offset() メソッドには欠点があります。body 部に border がある場合、Firefox だけ正しい要素位置が算出できないのです。この場合に jquery.js コードの jQuery().offset() で取得される top と left 値は、共に border 幅の値だけ過小になってしまいます。
そこで、エントリイを改めてその補正を行うコードを jquery プラグインとして作成して掲載する予定です。
■jQuery().offset() メソッドコード // html要素オブジェクトに getBoundingClientRect が定義されていれば、 // つまりブラウザが getBoundingClientRect メソッドに対応していれば、 5690:if ( document.documentElement["getBoundingClientRect"] ) // jQuery().offset() メソッドを prototype オブジェクトに組み込む 5691: jQuery.fn.offset = function( options ) { // インスタンスに登録されている最初の要素への参照を elem に登録する。 5692: var elem = this[0]; 5693: // elem がないか、document がないならば 5694: if ( !elem || !elem.ownerDocument ){ 5695: return null; // null を返す。 5696: } 5697: 5698: if ( options ) { // 引数 options があれば 5699: return this.each(function( i ) { // 対象要素を巡回処理 // setOffset クラスメソッドを呼び出す。 5700: jQuery.offset.setOffset( this, options, i ); 5701: }); 5702: } 5703: // 対象要素が body の場合 5704: if ( elem === elem.ownerDocument.body ) { // body を引数に bodyOffset クラスメソッドを 起動する 5705: return jQuery.offset.bodyOffset( elem ); 5706: } 5707: // インスタンスに格納されている最初の要素が document.body ならば // jQuery.offset.bodyOffset(document.body) の返値を返す。 // ローカル変数定義。 /* インスタンスに格納されている最初の要素の getBoundingClientRect() * オブジェクトへの参照を box に 、 document への参照を doc に、 * document.body への参照を body に 、html 要素への参照を docElem に、 * html.clientTop 又は document.body.clientTop 又はゼロを clientTop に * html.clientLeft 又は document.body.clientLeft 又はゼロを clientLeft に、 * それぞれ代入する。 */ 5708: var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement, 5709: clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0, // ローカル変数定義の続き /* top として、計測対象要素の top 値 + スクロール値 - clientTop を * left として、計測対象要素の left 値 + スクロール値 - clientLeft を * 代入する。 */ 5710: top = box.top + (self.pageYOffset || jQuery.boxModel && docElem.scrollTop || body.scrollTop ) - clientTop, 5711: left = box.left + (self.pageXOffset || jQuery.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft; // 返値定義 5712: 5713: return { top: top, left: left }; 5714: }; 5715:// ブラウザが getBoundingClientRect に対応していない場合 5716:} else { // #5717~5783 は #5691~5714 に同じである。 5717: jQuery.fn.offset = function() { 5718: var elem = this[0]; 5719: 5720: if ( !elem || !elem.ownerDocument ){ 5721: return null; 5722: } 5723: 5724: if ( options ) { 5725: return this.each(function( i ) { 5726: jQuery.offset.setOffset( this, options, i ); 5727: }); 5728: } 5729: 5730: if ( elem === elem.ownerDocument.body ) { 5731: return jQuery.offset.bodyOffset( elem ); 5732: } 5733: jQuery.offset.initialize(); 5734: // ローカル変数定義 /* elem に インスタンスに登録されている最初の要素(対象要素)への参照を、 * offsetParent に elem の offsetParent への参照を、 * prevOffsetParent に elem への参照を、doc に document への参照を、 * docElem に html 要素への参照を、body に document.body への参照を、 * defaultView に document.defaultView への参照を、prevComputedStyle に * document.defaultView.getComputedStyle(elem, null) オブジェクトへの参照を、 * top に対象要素の offsetTop 値を、left に対象要素の offsetLeft 値を * それぞれ代入する。 */ 5735: 5736: var offsetParent = elem.offsetParent, prevOffsetParent = elem, 5737: doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement, 5738: body = doc.body, defaultView = doc.defaultView, 5739: prevComputedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle 5740: top = elem.offsetTop, left = elem.offsetLeft; 5741: // 対象要素の親要素を elem に代入し、elem が body で html でもない限り // 巡回走査する。 5742: while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) { 5743: if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) { 5744: break; 5745: } 5746: // computedStyle 変数に対象要素の算出スタイルオブジェクトへの参照を代入する。 5747: computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle; // スクロール値が重複加算されるのでその都度減算する。 5748: top -= elem.scrollTop; 5749: left -= elem.scrollLeft; 5750: // 親要素が offsetParent ならば 5751: if ( elem === offsetParent ) { // 対象要素の親要素の offsetTop 値と offsetLeft 値を加算する。 5752: top += elem.offsetTop; 5753: left += elem.offsetLeft; 5754: /* 対象要素が 対象要素が table でも td でも th でもなく、かつ * doesNotAddBorder が true の場合、または、対象要素が table か * td か th で、かつ doesAddBorderForTableAndCells が true ではない場合 */ 5755: if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName)) ){ // 対象要素の親要素の border 幅値を top と left にそれぞれ加算する。 5756: top += parseInt( computedStyle.borderTopWidth, 10) || 0; 5757: left += parseInt( computedStyle.borderLeftWidth, 10) || 0; 5758: } 5759: // 対象要素の offsetParent の offsetParent を 変数 offsetParent に代入 5760: prevOffsetParent = offsetParent, offsetParent = elem.offsetParent; 5761: } 5762: // subtractsBorderForOverflowNotVisible が true で、overflow 属性値が // visible ではない場合には 5763: if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ){ // 対象要素の親要素の border 幅値を top と left にそれぞれ加算する。 5764: top += parseInt( computedStyle.borderTopWidth, 10) || 0; 5765: left += parseInt( computedStyle.borderLeftWidth, 10) || 0; 5766: } 5767: // 対象要素の親要素の算出スタイルオブジェクトを prevComputedStyle 変数に // バックアップしておく。 5768: prevComputedStyle = computedStyle; 5769: } 5770: // 対象要素の親要素の position 属性値が relative または static ならば、 5771: if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ){ // 最上位の offsetParent である document.body の offsetTop/Left 値を加算する。 5772: top += body.offsetTop; 5773: left += body.offsetLeft; 5774: } 5775: // ブラウザが position : fixed に対応しており、 // かつ、対象要素の親要素の position 属性値が fixed ならば 5776: if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ){ // html.scroll 値か body.scroll 値の大きい方を top/left 値に加算する。 5777: top += Math.max(docElem.scrollTop, body.scrollTop); 5778: left += Math.max(docElem.scrollLeft, body.scrollLeft); 5779: } 5780: 5781: return { top: top, left: left }; 5782: }; 5783:}
jQuery インスタンスメソッドである jQuery().offset や jQuery().position の解読を行おうと思い立って、jquery.js コードを睨み続けた結果、改めて offsetTop、offsetLeft について検討を加える必要があると思われました。
偶々、W3C による CSSOM View Module Working Draft が 2009年8月4日に公表されていたこともそうする必要性を高めました。 ( CSSOM View Module 作業草案 )、
たとえ「作業草案」とはいえ、拠って立つことのできる offsetTop/Left プロパティの定義として受け止めることの出来る内容となっています。こうして、この定義によって各ブラウザの差異を検証出来ることも、offsetTop/Left について再考するきっかけとなりました。
※ 以下は意訳であり、レイアウトボックスに関する名称は下図に拠り一部日本語化しました。
上の CSSOM VM 草案の定義で押さえておかねばならないことは、第一に document.body.offsetTop/Left の返値を常にゼロと定義したことです。つまり、body 要素に margin、padding あるいは border があろうがなかろうが、それらとは無関係にゼロとしたことが重要な点です。
別のエントリイ(offsetLeft,offsetTop,offsetWidth そして offsetHeight──静的配置要素の絶対位置を確実に取得する方法について)で詳細に述べたように、FireFoxは、その最新版(Ver 3.5.7)においても body 要素に border が存在する場合には、document.body.offsetTop/Left は何とボーダー幅のマイナス値を返してしまいます。これは CSSOM VM 草案を待たずとも、奇妙な仕様と言わざるを得ません。
因みに、Windows 系の他のブラウザ(IE8、Opera10.10、Chrome 3.0.195.38、safari4.0.4)の標準モード及び後方互換モードでは、Firefox のような奇妙な演算は起こらず、たとえ body 部に border が設定されていたとしても、document.body.offsetTop/Left は全てゼロを返します。Firefox だけが「マイナス border-width」 という奇妙な値を返します。
第二に、確認を含めて押さえておくべきことは、分かりやすく記述すれば A.offsetTop/Left の値は、A.offsetParent 要素のパディング辺と A.offsetTop/Left が起動された A 要素の border 辺との間のピクセル数になる、と定義されていることです。
この点では msdn の エレメントの大きさと位置を測定する に掲載されている図は、明らかに間違っています。(その図では、A.offsetParent のパディング辺から A のパディング辺までの距離を A.offsetTop/Left としています。これは 10 年以上前から掲載されている図ですが、当時の IE 5 あたりの、今日呼ぶところの互換モード仕様に基づくものと解釈することも出来ない、誤った図になっています。)
以上の 2 点は、offsetTop/Left を理解し、使いこなす上で極めて重要な基礎的なことであり、コード作成上 offsetTop/Left を使用して要素位置を知り、その結果を踏まえて何らかの要素を配置する場合には、ブラウザ固有の「偏差」(バイオス)によってずれが生じないように留意する必要があります。(ここでは敢えて「バグ」と言う表現は使わないことにします。何故ならば、CSSOM View Module はまだ作業草案段階であって、勧告に至っていないからです。)
要素の正確な位置を知りたい場合( 絶対配置、相対配置又は静的配置のいずれの場合においても )、あるいはそれを踏まえて適正な位置に任意の要素を配置しようとする場合、ブラウザ毎に個別に対応するのは大変な手間となります。そこで様々な W3C 勧告が作られ、それを基準としてブラウザが作られてきました。しかし、未だに各ブラウザごとに固有のバグがあるため(特に IE では多数のバグがある)、バグ対策が不可欠となります。
また、IE で初めて採用され、その後各ブラウザが採用した、offsetParent や offsetTop/Left プロパティは、要素の位置を知るために非常に有効な読み取り専用プロパティですが、現時点では標準的仕様さえ存在しないため、利用に当たっては適正な個別対策が必要となります。
jquery.js では、要素のドキュメント上の絶対座標を取得するメソッドとして jQuery().offset() を、また基準要素( つまり offsetParent 要素 )からの相対座標を取得するメソッドとして jQuery().position() を、それぞれ用意しています。また、これらのメソッド内で jQuery.offset オブジェクトの各プロパティが利用されます。(jquery.js ver 1.3.2 #4172~4197)
以下にこれらのコード解読を行いますが、その前に、jquery.js の位置算出方法の特徴に触れておきたいと思います。注目すべきことは 2 つあります。
第一に、getBoundingClientRect() メソッドという、余り見かけないメソッドを使うことによって、位置取得コードを非常に簡単にしていることです。このメソッドは(おそらく)全てのブラウザで同一の値が得られるので、クロスブラウザ対策が不要なのだと思います。
第二に、getBoundingClientRect() メソッドに対応していないブラウザへの対策として、良く行われているようなブラウザ判別法を採用していないことです。
よく見かけるコードでは、ブラウザ毎や描画モード毎に条件分岐させて、それ毎のプロパティ値を取得します。しかし、jquery.js はそのような伝統的手法を採用しません。
まず、位置算出対象とする HTML 文内に、Javascript コードによって visibility:hidden; position:absolute 状態の複数の要素( 要素 A 及びその offsetParent となる B 要素など )を挿入します。これによって、ブラウザ毎に位置算出値が異なる可能性を持った要素を作ります。
次に、A や B の offsetTop や offsetLeft 値を取得し、それが正しい値とならない場合にのみ、ブラウザによって描画され適用されたスタイル値( 算出スタイル値 )を取得して、正しい値を求めるのです。
このように CSS 算出スタイル値、つまり実際に描画された状態における CSS 値を利用することで、ブラウザのバグや固有のプロパティ仕様を「あるがままに受け入れ」、結果としての要素位置を算出している訳です。
なお、Windows 系の主要ブラウザ( IE、Firefox、Opera、safari、Chrome )では、標準モードでも互換モードでも、全て getBoundingClientRect() メソッドをサポートしているので、offsetParent/Top/Left プロパティを利用する位置算出コードは利用されないことになります。
もう一つ、コード解読を行う前に、 IE の名誉のために是非とも触れておかねばならないことがあります。
上に述べた jquery.js で定義された メソッドやオブジェクトにおいて、要素位置取得用に利用されるメソッドとプロパティ ── getBoundingClientRect()、offsetParent、offsetTop、offsetLeft ── は、全て IE が最初に実装し、他のブラウザも追随して採用した、という事実です。
Netscape Navigater が初めて採用し、しかし他のどのブラウザも採用しなかった layer オブジェクトとは対照的に、当時の IE4 や IE5 が意欲的に定義し、採用した所謂 DHTML モデルが、今日主流となっている W3C 勧告の DOM や CSS に至る起爆剤となったことは、過去において、IE が勝手な仕様を作り続けてきたことが混乱を招来してきたとしても、記憶に留めておくべき重要な事実でしょう。
なるほど、あらゆる管理機能にアクセスすることの出来るフォルダが作成できる!
新規フォルダを作りその名前を GodMode.{ED7BA470-8E54-465E-825C-99712043E01C} とするだけの簡単な操作で、コントロールパネルに似た特殊フォルダが作成され、その中には膨大な数の Windows 管理機能のショートカットリストが格納されるのだ。
名称がいかにも誇大妄想的とはいえ、これはなかなか使えるかもしれない。
詳細はこちらの記事 「Windows 7」の管理機能を集約--「GodMode」の存在が明らかに - OS/プラットフォーム - ZDNet Japan に掲載されている。
Microsoft と Google の2強と iPhone 一人勝ちの2009年で更に勢いを得た Apple。これらの3社がそれぞれに OS、クラウド、スマートホンにおいて仕掛ける様々な展開がまず注目される。
Windows 7 は一気に普及し、定着していくだろうし、Chrome OS が無料で登場すれば確実に台風の目になるだろう。iPhone は強固な牙城を築いた日本の「ケータイ」の有り様を変えつつあるが、Apple 社は次の一手(ディスプレイの更なる大型化やiPhone 技術を活かしたタブレットPCの投入など)を打ってくるかもしれない。
クラウドコンピューティングもますます進行し、もはやクラウドを使っていることさえ意識しないようになるかもしれない。
ハードにおいては、何と言っても USB 3.0 の普及が注目される。今年の年末商戦段階では、パソコンの標準インターフェースとなっているだろうし、ハードディスクや DVD プレーヤ、そしてその頃には標準装備となっているであろう Bluray ディスクドライブも USB 3.0 接続が当たり前になるだろう。
SSD (フラッシュメモリドライブ) の低廉化も注目に値する。裏返せば価格低下が進まなければ、ブレイクスルーしないだろう。
クラウドコンピューティングは、安心して使えるとはどうしても思えない。民間企業に全てを委ねてしまうクラウドには、どうしても合点がいかないのだ。
「データをクラウドに蓄積することは銀行にお金を預けることと同様に安心して良いのだ」との意見もある。確かに「信用」の問題なのだが、一企業の都合でどうにでもなってしまうかもしれないクラウドには、どうしても不安を拭いきれない。
そんなことを言っても、そもそも 1 つのクラウドサービスである無料 Fc2 ブログを 5 年以上使い続けて、その恩恵にどっぷりと浴しているのに「自己矛盾も甚だしい」と切り替えされるとにべもないのだが・・・
個人ユースで代表的はクラウドサービスは、Web メール・ブログ・掲示版・SNS などだろう。
特に Gmail サービスは無料で 7 GB ものにディスクスペースが使えることから、全てのメールを Gmailに統合して一元管理する方法があちこちで紹介されている。しかし私はその方法を選択することには二の足を踏み続けている。
例えば、5年以上使い続けてきたある掲示版が昨年末に閉鎖された( kidd.jp )。そしてその閉鎖を知ったのは、迷惑記事投稿が度重なるので、記事削除を行うために偶々 kidd.jp/bbs にアクセスした際であった。管理のためにアクセスして初めて 2009 年で閉鎖されることを「知った」のだ。
閉鎖に際してメールによる閉鎖通知が来るでもなく、掲示版データのバックアップなどに関する案内がユーザーに送られるでもなく、「会社の都合」により一方的に閉鎖されたのである。
こうした事例1つをとっても、クラウドは根本的に信頼できないと思うのだ。
つづく
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が開きます。