excanvas.js のハマリどころ, G_vmlCanvasManager.initElement の使い方

追記: uupaa-excanvas.js をリリースしました。
excanvas.js は IEcanvas 要素のエミュレートを行う Google 謹製の JavaScript ライブラリです。

このライブラリは、最初のとっかかりが結構難しいです。

実体験を元に、ハマリポイントを列挙してみます。

動的に生成したcanvas要素は、すぐにドキュメントツリーに追加し、その場で初期化する必要がある。

以下が、動的にcanvas要素を生成し2Dコンテキストを取得する例です。が…

var e = document.createElement("canvas");
document.body.appendChild(e);
var ctx = e.getContext("2d");

上記のコードでは、IEでは動作しません(Firefox,Safari,OperaはOK)。

動的に生成したcanvas要素は、G_vmlCanvasManager.initElement(element) で初期化する必要があります。
初期化はドキュメントツリー追加後に実行します。が…

var e = document.createElement("canvas");
document.body.appendChild(e);
G_vmlCanvasManager.initElement(e); // canvas要素の初期化
var ctx = e.getContext("2d");

実は、上記のコードも動作しません。
G_vmlCanvasManager.initElement(element) は初期化されたcanvas要素を返すため、戻り値に対して getContext() を行います。

var e = document.createElement("canvas");
document.body.appendChild(e);
e = G_vmlCanvasManager.initElement(e); // 初期化された要素が返るので、eに再代入
var ctx = e.getContext("2d");

これでやっと動作するようになります。

initElement は兄弟を皆殺しにする

先ほど、「すぐにドキュメントツリーに追加し、その場で初期化する必要がある。」と書きましたが、その理由をここで述べます。

このような要素片を生成するためには…

<div class="window-bone">
  <canvas class="window-canvas"></canvas>
  <div class="window-body"></div>
  <div class="window-title"></div>
</div>

こんな感じのコードをイメージすると思いますが…

var context = document.body;
var bone, canvas, body, title;
bone   = document.createElement("div");
canvas = document.createElement("canvas");
body   = document.createElement("div");
title  = document.createElement("div");
context.appendChild(this.db.bone);
bone.appendChild(canvas);
bone.appendChild(body);
bone.appendChild(title);
bone.className   = "window-bone";
body.className   = "window-body";
canvas.className = "window-canvas";
title.className  = "window-title";
if (ie) {
  canvas = G_vmlCanvasManager.initElement(canvas);
}

実行結果は、なぜかこのような要素片になります。

<div class="window-bone">
  <canvas class="window-canvas"></canvas>
</div>

実は、G_vmlCanvasManager.initElement(element) を実行すると、内部で走る G_vmlCanvasManager_::fixElement_() によりelementの兄弟要素や子孫要素が抹殺されます。

ですので、クロスブラウザなコードは以下となります。

var context = document.body;
var bone, canvas, body, title;
bone   = document.createElement("div");
canvas = document.createElement("canvas");
body   = document.createElement("div");
title  = document.createElement("div");
context.appendChild(this.db.bone);
bone.appendChild(canvas);
if (ie) {
  canvas = G_vmlCanvasManager.initElement(canvas);
}
bone.appendChild(body);
bone.appendChild(title);
bone.className   = "window-bone";
body.className   = "window-body";
canvas.className = "window-canvas";
title.className  = "window-title";

ほんのさわりだけの説明になりましたが、excanvas.jsを初めて使う人は、「絵が出ない」「中身が消える」といった奇怪な現象に出会うかもしれません。

excanvasのバグだ!」とあわてる前に、このブログエントリを思い出してください。