今回はスクリプトの書き方というより、スクリプトを書くための基礎となる知識について解説します。LZXでのスクリプトの記述形式は、<script>、<method>、<handler>といったスクリプト記述用タグを使ったり、属性として記述(たとえばonclick="~")するなど主に4種類あります。
<script>
<script>内に書かれたスクリプトは、アプリ起動時に最初に実行されます。どのような条件でも必ず実行されるので、アプリ全体で使うような変数や関数を定義する場合に使います。<script>はどこにでも書けるわけではなく、<canvas>直下にのみ書けます。
グローバル変数の定義
<script>内にグローバル変数を定義することができます。ここでいうグローバルとは、ソースコードのどこからでも使えるという意味です。
リスト1ではtxt変数に格納された文字列を参照するためにコンストレイント式を使っています。グローバル変数であるtxt変数の内容はいつでもどこでも参照できます。
ちなみにリスト1にあるような<text text="…">という書き方は<text>…</text>と同じ意味です。
グローバル関数の定義
<script>内にグローバル関数を定義することができます。
リスト2ではボタンをクリックした時に、<script>内に定義したmoveWin()という自作関数を実行するように記述しています。moveWin()内の「win.x」というのは「winという名前の付いたタグ(id="win")のx属性の値」の意味です。moveWin()はウインドウを現在位置から20ピクセル右に移動するようにした関数です。
<method>
<method>を使うと、スクリプトを特定のタグに関連付けて定義することができます。<script>と違って、呼び出されない限り実行されません。
<method>にはname属性で名前を付ける必要がありますが、タグに定義されている既存メソッド名と重ならないように注意します。重なると既存メソッドの機能を上書き(オーバーライド)することになります。タグがどのようなメソッドを持っているかはLZXリファレンスを参照してください。
メソッド名の設定はたとえば<method name="sample">のように記述し、そのメソッドを実行する場合はsample()とカッコ付きで記述します。
リスト3の例では全く同じ内容のmoveWin()メソッドを<canvas>直下とウインドウタグ内に定義しています。ボタン1を押すと(1)のmoveWin()メソッド、ボタン2は(2)のmoveWin()メソッドをそれぞれ実行します。どちらのボタンも動作結果は同じです。
メソッドはargs属性を使って引数を受け取ることができます。リスト4では「moveWin(20)」実行時に20という値がmoveWin()メソッドに渡され、メソッド側でincrementという変数で値を受け取っています。
メソッドはreturnを使って値を戻すことができます。リスト5では、2つの数値の足し算結果を戻すadd()メソッドを作った例です。ボタンを押すとadd()メソッドが実行され、戻された2+3の結果が<text>に表示されます。
<handler>
<handler>はonclickのようなイベント発生時に実行するスクリプトを記述するためのタグです。名前はname属性で設定します。名前の付け方は自由ではなくonで始まるイベントハンドラ名を付けます。イベントハンドラ名にはonclickやonmouseoverといった「on+イベント名」や、onxやonwidthなどの「on+属性名」があります。
イベントハンドラのスクリプトの記述方法は2種類あります。「onclick="・・・"」のようにタグの属性として記述する方法と、<handler>タグ内に記述する方法です。タグの属性として記述する場合、スクリプトを直接記述する方法(リスト6)や、実行したいスクリプトのあるメソッドを記述する方法(リスト7)があります。リスト7の書き方はメンテナンスしやすいです。
これまでのサンプルで出てきた<button>でのonclick="win.moveWin(20);"もイベントハンドラですが、処理が多くなって行数が増えるとこのような簡単な記述はできにくくなります。<handler>を使うとイベントハンドラのスクリプト部分を独立させて別途記述することができます。リスト5で既に出ていますね。
リスト9のボタン1とボタン2のイベントハンドラonclickの記述形式は異なっていますが、全く同じ意味です。onclick時にwin.moveWin(20)が実行されます。
複数のイベントハンドラ
メソッドと違いイベントハンドラの場合は同じ名前で複数の定義ができます。複数の同名イベントハンドラは上書きにはならずすべてが実行されます。
リスト10の例では、onclickというイベントハンドラの記述が4ヶ所にありますが、ボタンクリック時にすべて実行されます。つまりxとyがそれぞれ50に、widthとheightもそれぞれ100に同時に設定変更されます。
idとname、オブジェクト指定方法
これまでのサンプルでもしょっちゅう出ていたので何となくご理解いただいているかもしれませんが、idとname、およびオブジェクトの指定方法について説明します。ここでいうオブジェクトとは簡単に言うとタグのことです。
あるタグに対して動作させたり属性値を取得したりするにはそのタグを指定しなければなりません。そのタグを指定するためにid属性やname属性で名前を付けます。nameは同一階層でのみ重複しなければ良く、idはアプリ内で重複してはいけない、という違いがあります。さらに、nameはLZXの階層構造を意識した階層的な指定が必要なのに対し、idはその名前のみの直指定でOKという違いがあります。
nameの場合の指定方法
nameの場合、相対指定と絶対指定があります。相対指定は階層上の自身の位置から相手の位置へのパスを指定します。ファイルシステムでいうと「../../tmp」のような指定の仕方です。一方、絶対指定はルート要素のcanvasから降りていくパスを指定します。ファイルシステムでいうと「/usr/local/tmp」のようにルートディレクトリからフルパスで指定するのと同じです。
自分自身はthisで指定します。
階層構造の親子関係の中では親は1つなので名前を指定する必要はなくparentで指定できます。親の親はparent.parentで、2階層上にあがることを意味します。
親の子、つまり兄弟タグは複数存在できるので名前で識別するしかありません。そのためにnameで名前を付けます。もちろん兄弟だけでなく親の親の子、親の兄弟の子など識別するためにはすべて名前が必要です。
リスト11ではA,Bのビューは兄弟タグなので同じ名前は使えませんが、それぞれの子ビューa,b,cは階層が違うので同じ名前が使えます。
表1 リスト11の階層構造での指定方法
指定の内容 | 相対指定 | 絶対指定 |
(1)から(2) | this.b | canvas.A.a.b |
(1)から(3) | this.b.c | canvas.A.a.b.c |
(1)から(5) | parent.parent.B.a.b.c | canvas.B.a.b.c |
(3)から(1) | parent.parent | canvas.A.a |
(3)から(2) | parent | canvas.A.a.b |
(3)から(4) | parent.d | canvas.A.a.b.d |
(5)から(3) | parent.parent.parent.parent.A.a.b.c | canvas.A.a.b.c |
リスト12では全く同じ内容の自作メソッドmoveRight()が3ヶ所に記述されています。moveRight()自体は自分自身を右に動かす処理が書かれています。
ボタン(1)はparent.moveRight()となっているので、親のmoveRight()つまりメソッド2が実行されます。
ボタン(2)はparent.parent.win2.moveRight()で、親の親の子win2のmoveRight()つまりメソッド1が実行されます。
ボタン(3)はthis.moveRight()なので自分自身のmoveRight()が実行されます。
moveRight()メソッド内のthisは<method>自身ではなく<method>が定義されている親タグのことを指します。<method>自身はビューではなく親タグの付属物であり、つまり親タグ自身であるということになっているためです。この点は<handler>についても<method>と同じです。これらはソースの見た目の階層構造と異なるので注意が必要です。
※ここで出ているanimate()はアニメーション処理をするメソッドです。this.animate('x',20,300,true)とは、「x」属性の値を「20」ピクセルに「300」ミリ秒かけて変化させるという意味です。最後の「true」は変化後の値に対して相対的に動くという意味です。falseだとx=20になりますが、trueだとx値が20ずつ増加することになります。アニメーション系は後の記事で解説します。
idの指定方法
idを使う場合、アプリ内で重複しないたった1つの名前を設定します。idで名前を付けられたタグを指定するには階層構造を一切気にすることなくその名前のみでOKです。
リスト13はリスト11と同じですが、idで付けた名前は重複できないのでビューBの子ビュー達の名前をa,b,c,からe,f,gに変えています。表2では表1と異なり名前のみで超簡単に指定できることがわかります。相対指定だと階層構造が変わったときにparent.parent・・・といった記述の修正が大変なので、問題がない限りid指定で良いでしょう。筆者も基本的にidを使っていますし、本連載記事に出てくるサンプルコードもほとんどid指定です。
表2 リスト13の階層構造での指定の方法
(1)から(2) | b |
(1)から(3) | c |
(1)から(5) | g |
(3)から(1) | a |
(3)から(2) | b |
(3)から(4) | d |
(5)から(3) | c |
リスト14はリスト12の相対指定の部分をid指定に変更したものです。リスト12との変更点は2ヶ所で、2行目のウインドウ2のnameをidに、13行目のボタン(2)の相対指定(parent.parent.win2.moveRight())を絶対指定(win2.moveRight())に変えています。
setAttribute()
スクリプトを記述するときに頻出するメソッドsetAttribute()について説明しておきます。このメソッドは文字通りattribute(=属性)をset(設定)するものです。
文法的にはsetAttribute(属性名,値)となります。たとえばx属性の値を100にするにはsetAttribute('x',100)、width属性の値を200にするにはsetAttribute('width',200)と書きます。
setAttribute()の超重要な機能としては、実行時に「on+属性名」のイベントが自動的に発生する点です。setAttribute('x',100)実行時には「onx」イベントが、setAttribute('width',200)実行時には「onwidth」イベントが発生します。それをイベントハンドラで受信して何か処理をする、というイベント駆動のプログラムを書くことができます。イベントが電波で、イベントハンドラがアンテナのようなイメージです。
リスト15サンプルのボタンを押すと縦向きの赤青黄のビューが位置を変えて横向きになります。ボタンクリック時にボタン自身のx値を変えただけ(1)なのですが、onxというイベントが発生したために他のビューに次々と影響(2)(3)(4)を与えています。
※
<handler>にあるreference属性には、拾いたいイベントが発生するタグ名を指定します。referenceがなければデフォルト動作として親タグのイベントに反応しますが、referenceがあれば親タグではなく指定されたタグのイベントに反応します。
(1)・・・ボタンクリック時にボタンのxを100に変更(=右に移動する)。→ ボタンbtnにonxイベントが発生する。
(2)・・・ボタンbtnのonxイベントを受信して実行。→ ビューv1にonxイベント、onyイベントが発生する。
(3)・・・ビューv1のonxイベントを受信して実行。→ ビューv2にonxイベント、onyイベントが発生する。
(4)・・・ビューv2のonyイベントを受信して実行。→ ビューv3にonxイベント、onyイベントが発生するがそれを受信するイベントハンドラはどこにもないので無視される。