Adobe AIR HTML+JSでコードを書くときの注意点

さて、引き続きAdobe AIRネタです。

Adobe AIRのアプリケーションは、独自のマークアップ言語MXMLとActionScriptの組み合わせで作る方法の他に、HTML+JavaScriptでつくる方法もあります。

レンダリングエンジンにはWebKitが採用されているため、ChromeのWebインスペクタなどでUIデザイン等を行うことができます。(JavaScriptのデバッグについては、AdobeAIR特有のAPIの都合上無理な場合が多いです)HTMLはHTML5に対応しており、JavaScriptもChromeのものとほぼ同じものを使用することができます。

…が、残念なことにDOM周りについては通常のHTMLよりも制限を受ける場合が多く、またその制限についてググってもあまりヒットしません。公式のドキュメントもすごく読みづらいし。ということで、HTML+JSでAdobe AIRアプリケーションを開発する際にハマるであろうポイントを以下にまとめます。
気づいたことがあったらどんどん追記していく予定です。

動的に生成した要素のイベントは発生しない

表題のとおりですが、動的に生成した要素のイベントは発生しません。

$('#hoge').append('<div onclick="alert(\"Hello!\");">click</div>');
例えばこんなコードがあったとして、このコードで生成されたdiv要素をクリックしても警告は表示されません。 イベントを追加したい場合は、予めHTMLファイルに記述しておく必要があります。

が、実際動的に生成した要素にイベントを追加したい場合などいくらでもあります。そんな時はjQueryのdelegeteを使ってイベントを使用します。
delegateは、子要素で発生したイベントがイベントバブリングで上がってきた時に、それをキャッチしてイベントを発火させることができる仕組みです。つまり、子要素が存在しない場合でもイベントを追加することができ、あとから追加する子要素に対しても自動的にイベントが追加されたような状態にすることができます。

以上を踏まえて、上記のコードをAdobe AIRでも動作するように書き換えると、

$(document).delegate('div', 'click', function(e){ alert("Hello!"); });
$('#hoge').append('<div>click</div>');
このように、delegateはイベント発生源が指定されたセレクタに合致する場合にイベントを発火させる仕組みになります。上記の例では、いくつdivを追加しても、必ず親のdocumentにイベントがバブリングされるので、div要素自体にイベントを設定する必要はありません。

このdelegateを、静的に存在する(もとからHTMLに記述されている)要素に設定しておくと、動的に生成した要素に対してイベントを発生させることが出来るようになります。

IME入力中はキー入力イベントが発生しない

これも表題のとおりですが、IME入力中はonKeyDown、onKeyUp、onKeyPress等のイベントが発生しません。ChromeやSafariなど、他のWebKitエンジンを持ったブラウザでは普通に発生するので、Adobe AIRの独自の仕様と考えられますが、これがなかなか厄介で、入力が完了するまでイベントが発生しません。つまり、入力が確定した段階でもイベントは発生しません。入力確定後の次の入力ではじめてイベントが発生します。

イベントがそもそも発生しないので、対策としては一定間隔ごとにtextareaやinputの値を読み込んで変化を見るしかありません。非常に頭の悪い方法ですが、簡単な解決策はこれしか考えつきませんでした。

一方、少し複雑ですが、DOMCharacterDataModifiedは入力中もしっかり発生するようなので、div要素をcontentEditableにして、そのdiv要素にDOMCharacterDataModifiedを指定すると、IME入力中のイベントも取得できるようです。ただし、contentEditableがかなりのクセモノなので、まともに使うには色々と面倒な事になりそうです(経験としては、先頭に謎の改行が入ったり、途中の改行が消せなくなったり、キャレット位置がぶっ飛んだり、といったところです)。

なお、DOMCharacterDataModifiedについてはググるといくつか資料が見つかりますので、それを参考にしてみるといいと思います。

NativeWindowにイベントを追加する

NativeWindow、要はHTMLoader.createRootWindowで生成したウィンドウに対してイベントを設定するには、

window.nativeWindow.addEventListener(
    air.Event.CLOSE,
    function(e){
        alert('close');
    });
のように記述する必要があります。windowにイベントを追加しても発火しません。また、親ウィンドウ(HTMLoader.createRootWindowを呼んだ側)からイベントを設定するには、以下のように記述します。
subwindow.stage.nativeWindow.addEventListener(
    air.Event.CLOSE,
    function(e){
        alert('close');
    });
上記のsubwindowは、HTMLoader.createRootWindowの戻り値(生成されたNativeWindow)です。

2012/04/14  追加「NativeWindowにイベントを追加する」

0 件のコメント :

コメントを投稿