これでできる! クロスブラウザJavaScript入門

第19回CSSOM View Module解説

こんにちは、前々回前回とアニメーションのお話でしたが、今回はアニメーションにも関連してくるCSSOM View Moduleという新しい仕様(2010年11月時点でWorking Draft)のお話です。

クロスブラウザの連載で草稿段階の仕様について触れることに不思議に思われるかもしれませんが、このCSSOM View Moduleは既存のブラウザの実装をベースとして、その細かな違いを吸収して一つの標準仕様としてまとめあげることを目標として策定が進められています。つまり、この仕様を学ぶことで、⁠現在の実装がどうなっているのか⁠⁠、⁠将来的にどういった実装になる予定なのか」ということを知ることができます。当然、今後大きな変更がある可能性はありますが、現状を整理するうえで重要な仕様なので詳しく見ていきたいと思います。

CSSOM View Moduleとは

まず、CSSOMとは、CSS Object Modelの略語で、つまりDOM(Document Object Model)のCSS版といえる仕様です。CSSOM自体はこの連載の第10回で扱った内容が相当します。

そして、CSSOM View ModuleはCSSOMから派生して、ドキュメントの見た目の解析・操作を提供する仕様となっており、特に要素の位置情報を扱ったAPIが定義されています。

この要素の位置については、長い間仕様が存在せず、多くがDOM Level 0と呼ばれています。実際にブラウザごとに細かな違いがあるため、クロスブラウザ対応において大きな障害となりやすい部分です。CSSOM View Moduleでは、これらのAPIを統合して、高い互換性を実現することを目標としています。

CSSOM View ModuleのAPI

では、CSSOM View ModuleでどういったAPIが定義されているのか見てみましょう。

まず、CSSOM View Moduleで定義されているAPIは次のとおりです。

  • AbstractViewインターフェース

    名前のとおり、最も抽象的なインタフェースで、APIはありません。DocumentViewとMediaが継承しています。

  • Mediaインターフェース

    MediaQuery用のインターフェースです。

  • ScreenViewインターフェース

    視覚的なメディア表現用のインターフェースです。Documentのdefault viewがこのインターフェースを実装しています。default viewというのはつまりwindowオブジェクト(ウェブブラウザにおけるグローバルオブジェクト)のことです。

  • Screenインターフェース

    出力デバイスのスクリーンの情報を扱うインターフェースです。ScreenViewがブラウザウィンドウ側の情報なのに対して、Screenはモニタ側の情報を扱います。

  • DocumentViewインターフェース

    DocumentViewインターフェースはdocumentオブジェクト用のインターフェースです。DOMとの連携において有用なAPIが定義されています。

  • Elementインターフェース拡張

    DOM Level 3 Coreで定義されているElementインターフェースの拡張APIが定義されています。

  • HTMLElementインターフェース拡張

    DOM Level 2 HTMLとHTML5で定義されているHTMLElementインターフェースの拡張APIが定義されています。

  • Rangeインターフェース拡張

    DOM Level 2 Traversal and Rangeで定義されているRangeインターフェースの拡張APIが定義されています。

  • MouseEventインターフェース

    DOM Level 2 Eventsで定義されているMouseEventインターフェースの拡張APIが定義されています。

簡単に言えば、まず一番外側にモニターがあって、その位置情報を持っているのがScreenインターフェース(screen)です。モニターの中にはブラウザのウィンドウがあります。その位置情報はScreenViewインターフェース(window)がもっています。さらにブラウザの中にはウェブページがあります。ページの位置情報を持っているのもやはりScreenViewインターフェース(window)です。ウェブページの中には要素があり、要素の位置情報を持っているのはElementインターフェースとHTMLElementインターフェースです。さらに、特定の範囲についての位置情報を扱うRangeに、マウスイベントなどの位置情報を扱うインターフェースがあります。

では、順番に各インターフェースを見ていきましょう

スクリーンの位置情報

モニターについての情報はグローバル変数のscreenオブジェクトから取得できます。

CSSOM Viewで定義されているのは、availWidth, availHeight, width, height, colorDepth, pixelDepthの6つです。

width, heightは実際のモニタのサイズ、availWidth, availHeightはタスクバーなどで隠れていない使用可能なサイズを表しています。

Firefox, Safari, ChromeではさらにavailLeft, availTopという独自プロパティも対応しており、どの部分にタスクバーなどがあるか推測することができます。

なお、IEは独自のプロパティをいくつか持っており、次のようなオブジェクトになっています。

IEのscreenオブジェクト(1600×1200のモニターの場合)
{
  bufferDepth : 0,
  deviceXDPI : 96,
  deviceYDPI : 96,
  fontSmoothingEnabled : true,
  logicalXDPI : 96,
  logicalYDPI : 96,
  systemXDPI : 96, // IE8以降
  systemYDPI : 96, // IE8以降
  updateInterval : 0,
  availHeight : 1170,
  availWidth : 1600,
  colorDepth : 32,
  height : 1200,
  pixelDepth : 32, // IE9以降
  width : 1600
}

ウィンドウの位置情報

ウィンドウの位置情報はwindowオブジェクト、すなわちグローバルオブジェクト自身から取得できます。

CSSOM Viewで定義されているプロパティ・メソッドは次のとおりです。

ScreenViewのインターフェース
  // viewport
  readonly attribute long innerWidth;
  readonly attribute long innerHeight;
  readonly attribute long pageXOffset;
  readonly attribute long pageYOffset;
  void scroll(long x, long y);
  void scrollTo(long x, long y);
  void scrollBy(long x, long y);

  // client
  readonly attribute long screenX;
  readonly attribute long screenY;
  readonly attribute long outerWidth;
  readonly attribute long outerHeight;

innerWidth・innerHeightはブラウザの内側の表示領域を表します。この値はスクロールバーを含まない点に注意が必要です。多くの場合、縦のスクロールバーが表示されているので、innerWidthの値を元に横幅いっぱいの要素を作るとスクロールバーの幅だけ はみ出してしまいます。outerWidth・outerHeightはブラウザのウィンドウ全体のサイズを表します。なお、これらについてIE 6~8は対応していません。

pageXOffset・pageYOffsetは表示領域において、ページのスクロール量を表します。つまりページを100pxだけスクロールしていたら100を返します。FirefoxやSafariとChromeはscrollX・scrollYという独自プロパティでも同じ値を取得することができます。やはり、IE 6~8は対応していません。

screenX・screenYはモニターを基点としたブラウザウィンドウの位置を表します。こちらもIE 6~8は対応していませんが、screenLeft・screenTopで同様の値を取得することが可能です。

scroll・scrollTo・scrollByはページを移動するメソッドです。scrollとscrollToはまったく同じ動作で、引数に渡したx座標、y座標に直接移動します。scrollByは引数に渡した値の分だけ現在の位置から移動します。こちらはIEを含めたブラウザで使用できます。

要素の位置情報

HTMLの各要素の位置情報は各要素のプロパティから取得できます。

CSSOM Viewで定義されているプロパティ・メソッドは次のとおりです。

Elementのインターフェース
           attribute long scrollTop;
           attribute long scrollLeft;
  readonly attribute long scrollWidth;
  readonly attribute long scrollHeight;

  readonly attribute long clientTop;
  readonly attribute long clientLeft;
  readonly attribute long clientWidth;
  readonly attribute long clientHeight;

  ClientRectList getClientRects();
  ClientRect getBoundingClientRect();

scrollTop・scrollLeftはその要素がスクロール可能な状態であったとき(その要素のスタイルにoverflow:autoとheightなどの値が適用されていて、その要素の中身がheight以上であった場合など)に、そのスクロール量を表します。このプロパティは代入が可能で、有効な値を代入することで実際にスクロールさせることができます。

標準のモードでは、document.documentElement(html要素)がデフォルトでスクロール可能な状態に相当するので、document.documentElement.scrollTopはページのスクロール量を表します。これは、前述のScreenViewにおけるpageYOffsetと一致します。

ただし、WebKit(Safari、Chrome)ではdocument.documentElement.scrollTopは常に0となり、document.body.scrollTopがスクロール量を表すので注意が必要です。

また、互換モードでは、どのブラウザもdocument.documentElementではなくdocument.body(body要素)がスクロール対象の要素となるため、document.body.scrollTop と pageYOffsetが対応することになります。

この部分はCSSOM Viewにおいて最も厄介なところです。まとめると、ページのスクロール量を取得したい場合は次のようなコードになります。

スクロール量の取得
var root = 'BackCompat' === document.compatMode ?
           document.body : document.documentElement;
var scrollY = window.pageYOffset || root.scrollTop;

IE 6~8以外は常にwindow.pageYOffsetが使えるのでそれを利用し、window.pageYOffsetが使えない場合は標準モードか互換モードかを見て、scrollTopを使用します。

scrollWidth・scrollHeightは表示領域から隠れた部分も含めた、その要素が占める領域を表します。

clientWidth・clientHeightはその要素の表示領域自体を表します。padding-widthは含みますが、border-widthとmargin-widthは含みません。

clientTop・clientLeftはその要素のスタイルに適用されたborder-widthの値を返します。

getClientRects・getBoundingClientRectはその要素のClientRectを取得するメソッドです。getClientRectsは折り返しを含むときに、その折り返しごとにRectオブジェクトを返します。getBoundingClientRectはその要素全体を含む領域を表します。

ClientRectはtop, right, bottom, left, width, heightといったプロパティを持ち、その要素の位置に関する情報をまとめて取得することができます。top, right, bottom, leftは現在の表示領域を基点とした値となるので、スクロール量を加算するとそのページでの絶対座標に相当します。

また、CSSOM ViewではHTMLElementのインターフェースも定義されています。ElementとHTMLElementの違いは、ElementがSVGなどHTML以外のDOMにも適用されるのに対して、HTMLElementは名前のとおりHTMLの要素のために用意されたインターフェースです。

HTMLElementはDOM Level 2 HTMLやHTML5などで定義されています。

HTMLElementのインターフェース
  readonly attribute Element offsetParent;
  readonly attribute long offsetTop;
  readonly attribute long offsetLeft;
  readonly attribute long offsetWidth;
  readonly attribute long offsetHeight;

まず、offsetParentはその要素の祖先を遡って、最初にスタイルにposition:static以外の値を持つ要素、もしくはdocument.bodyを示します(ただし、area要素の場合はmap要素を示します⁠⁠。

offsetTop・offsetLeftはoffsetParentからみた、その要素の位置を表します。

offsetWidth・offsetHeightはその要素のborder-widthを含んだ表示領域のサイズを表します。

マウスの位置情報

マウスの位置情報はマウスに関するイベント(onclickやmousedown、mousemoveなど)で取得したEventオブジェクトのプロパティから取得できます。

MouseEventのインターフェース
  readonly attribute long screenX;
  readonly attribute long screenY;

  readonly attribute long pageX;
  readonly attribute long pageY;

  readonly attribute long clientX;
  readonly attribute long clientY;
  readonly attribute long x;
  readonly attribute long y;

  readonly attribute long offsetX;
  readonly attribute long offsetY;

screenX・screenYはモニターを基点としたマウスの座標を表します。

pageX・pageYはそのページ全体の左上を基点とした現在のマウスの位置を表します。IE 6~8は対応していません。

clientX・clientYおよびx・yは表示されている領域の左上を原点としたマウスの位置を表します。pageX・pageYからスクロール量を引いた値に一致します。

offsetX・offsetYはクリックした要素(eventのtarget)からみたマウスの座標を表します。ただし、FirefoxはoffsetX・offsetYに対応しておらず、代わりにlayerX・layerYというプロパティを持っています。このoffset・layerはブラウザによってborder-width・padding-widthを含む含まないの差があるため、正確に値を取得するのは非常に厄介です。そのため、要素のどの位置をクリックしたのかを使用すること自体を避けてしまうのが無難な判断と言えます(実際、ほとんどの場合でclientX・clientYだけで十分なはずです⁠⁠。

なお、IEはpageX・pageYに相当するプロパティがないので、代わりにclientX・clientYにページのスクロール量を足して計算します。

IEでのpageY
var root = 'BackCompat' === document.compatMode ?
           document.body : document.documentElement;
var pageY = event.clientY || root.scrollTop;

まとめ

今回はCSSOM ViewからDOMの位置の扱い方を学びました。次回は今回学んだ内容を実際に使用する実例を中心に扱いたいと思います。

おすすめ記事

記事・ニュース一覧