OpenLaszloでマルチデバイス対応RIAを作ろう

第6回スクリプトの記述形式

今回はスクリプトの書き方というより、スクリプトを書くための基礎となる知識について解説します。LZXでのスクリプトの記述形式は、<script>、<method>、<handler>といったスクリプト記述用タグを使ったり、属性として記述(たとえばonclick="~")するなど主に4種類あります。

<script>

<script>内に書かれたスクリプトは、アプリ起動時に最初に実行されます。どのような条件でも必ず実行されるので、アプリ全体で使うような変数や関数を定義する場合に使います。<script>はどこにでも書けるわけではなく、<canvas>直下にのみ書けます。

グローバル変数の定義

<script>内にグローバル変数を定義することができます。ここでいうグローバルとは、ソースコードのどこからでも使えるという意味です。

リスト1ではtxt変数に格納された文字列を参照するためにコンストレイント式を使っています。グローバル変数であるtxt変数の内容はいつでもどこでも参照できます。

ちなみにリスト1にあるような<text text="…">という書き方は<text>…</text>と同じ意味です。

リスト1 グローバル変数の例
<canvas proxied="false" bgcolor="0xffffcc"> 
  <script>
    var txt = "こんにちは";
  </script>
  <simplelayout/>
  <text text="${txt}"/>
  <button text="${txt}"/>
  <checkbox text="${txt}"/>
</canvas>

リスト1サンプル

グローバル関数の定義

<script>内にグローバル関数を定義することができます。

リスト2ではボタンをクリックした時に、<script>内に定義したmoveWin()という自作関数を実行するように記述しています。moveWin()内の「win.x」というのは「winという名前の付いたタグ(id="win")のx属性の値」の意味です。moveWin()はウインドウを現在位置から20ピクセル右に移動するようにした関数です。

リスト2 グローバル関数の例
<canvas proxied="false" bgcolor="0xffffcc"> 
  <script>
    var txt = "移動";
    function moveWin(){
      var increment = 20;
      win.setAttribute('x', win.x + increment);
    }
  </script>
  <button text="${txt}" onclick="moveWin();"/>
  <window x="50" y="30" width="150" height="100" title="ウインドウ" id="win">
  </window>
</canvas>

リスト2サンプル

<method>

<method>を使うと、スクリプトを特定のタグに関連付けて定義することができます。<script>と違って、呼び出されない限り実行されません。

<method>にはname属性で名前を付ける必要がありますが、タグに定義されている既存メソッド名と重ならないように注意します。重なると既存メソッドの機能を上書き(オーバーライド)することになります。タグがどのようなメソッドを持っているかはLZXリファレンスを参照してください。

メソッド名の設定はたとえば<method name="sample">のように記述し、そのメソッドを実行する場合はsample()とカッコ付きで記述します。

リスト3の例では全く同じ内容のmoveWin()メソッドを<canvas>直下とウインドウタグ内に定義しています。ボタン1を押すと(1)のmoveWin()メソッド、ボタン2は(2)のmoveWin()メソッドをそれぞれ実行します。どちらのボタンも動作結果は同じです。

リスト3 メソッドの例
<canvas proxied="false" bgcolor="0xffffcc"> 
  <!--(1)canvas直下の自作メソッド-->
  <method name="moveWin">
    var increment = 20;
    win.setAttribute('x', win.x + increment);
  </method>
  <button text="ボタン1" onclick="canvas.moveWin();"/>
  <button y="25" text="ボタン2" onclick="win.moveWin();"/>
  <window x="50" y="50" width="150" height="100" title="ウインドウ" id="win">
    <!--(2)windowタグの自作メソッド-->
    <method name="moveWin">
      var increment = 20;
      win.setAttribute('x', win.x + increment);
    </method>
  </window>
</canvas>

リスト3サンプル

メソッドはargs属性を使って引数を受け取ることができます。リスト4では「moveWin(20)」実行時に20という値がmoveWin()メソッドに渡され、メソッド側でincrementという変数で値を受け取っています。

リスト4 メソッドの引数

<canvas proxied="false" bgcolor="0xffffcc"> 
  <button text="ボタン" onclick="win.moveWin(20);"/>
  <window x="50" y="30" width="150" height="100" title="ウインドウ" id="win">
    <method name="moveWin" args="increment">
      win.setAttribute('x', win.x + increment);
    </method>
  </window>
</canvas>

リスト4サンプル

メソッドはreturnを使って値を戻すことができます。リスト5では、2つの数値の足し算結果を戻すadd()メソッドを作った例です。ボタンを押すとadd()メソッドが実行され、戻された2+3の結果が<text>に表示されます。

リスト5 メソッドの戻り値
<canvas proxied="false" bgcolor="0xffffcc"> 
  <!--足し算メソッド-->
  <method name="add" args="n1,n2">
    return n1+n2;
  </method>
  <simplelayout/>
  <button text="ボタン" >
    <handler name="onclick">
      var ans = canvas.add(2,3);
      res.setAttribute('text', ans);
    </handler>
  </button>
  <text id="res"/>
</canvas>

リスト5サンプル

<handler>

<handler>はonclickのようなイベント発生時に実行するスクリプトを記述するためのタグです。名前はname属性で設定します。名前の付け方は自由ではなくonで始まるイベントハンドラ名を付けます。イベントハンドラ名にはonclickやonmouseoverといった「on+イベント名」や、onxやonwidthなどの「on+属性名」があります。

イベントハンドラのスクリプトの記述方法は2種類あります。⁠onclick="・"」のようにタグの属性として記述する方法と、<handler>タグ内に記述する方法です。タグの属性として記述する場合、スクリプトを直接記述する方法(リスト6)や、実行したいスクリプトのあるメソッドを記述する方法(リスト7)があります。リスト7の書き方はメンテナンスしやすいです。

リスト6 タグの属性としてイベントハンドラを記述(1)
<view onclick="クリック時に処理するスクリプトをここに記述" />

リスト7 タグの属性としてイベントハンドラを記述(2)

<view onclick="doSomething()">・・・クリック時にdoSomething()メソッドを実行
  <method name="doSomething">
    スクリプトをここに記述
  </method>
</view>

リスト8 <handler>でイベントハンドラを記述

<view>
  <handler name="onclick">
    クリック時に処理するスクリプトをここに記述
  </name>
</view>

これまでのサンプルで出てきた<button>でのonclick="win.moveWin(20);"もイベントハンドラですが、処理が多くなって行数が増えるとこのような簡単な記述はできにくくなります。<handler>を使うとイベントハンドラのスクリプト部分を独立させて別途記述することができます。リスト5で既に出ていますね。

リスト9のボタン1とボタン2のイベントハンドラonclickの記述形式は異なっていますが、全く同じ意味です。onclick時にwin.moveWin(20)が実行されます。

リスト9
<canvas proxied="false" bgcolor="0xffffcc"> 
  <button text="ボタン1" onclick="win.moveWin(20)"/>
  <button text="ボタン2" y="25">
    <handler name="onclick">
      win.moveWin(20);
    </handler>
  </button>
  <window x="50" y="50" width="150" height="100" title="ウインドウ" id="win">
    <method name="moveWin" args="increment">
      win.setAttribute('x', win.x + increment);
    </method>
  </window>
</canvas>

リスト9サンプル

複数のイベントハンドラ

メソッドと違いイベントハンドラの場合は同じ名前で複数の定義ができます。複数の同名イベントハンドラは上書きにはならずすべてが実行されます。

リスト10の例では、onclickというイベントハンドラの記述が4ヶ所にありますが、ボタンクリック時にすべて実行されます。つまりxとyがそれぞれ50に、widthとheightもそれぞれ100に同時に設定変更されます。

リスト10 複数の同名イベントハンドラ
<canvas proxied="false" bgcolor="0xffffcc"> 
  <simplelayout/>
  <button text="ボタン" onclick="v.setAttribute('x',50)">
    <handler name="onclick">
      v.setAttribute('y',50);
    </handler>
    <handler name="onclick">
      v.setAttribute('width',100);
    </handler>
    <handler name="onclick">
      v.setAttribute('height',100);
    </handler>
  </button>
  <view id="v" x="20" y="20" width="20" height="20" bgcolor="red"/>
</canvas>

リスト10サンプル

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.bcanvas.A.a.b
(1)から(3)this.b.ccanvas.A.a.b.c
(1)から(5)parent.parent.B.a.b.ccanvas.B.a.b.c
(3)から(1)parent.parentcanvas.A.a
(3)から(2)parentcanvas.A.a.b
(3)から(4)parent.dcanvas.A.a.b.d
(5)から(3)parent.parent.parent.parent.A.a.b.ccanvas.A.a.b.c
リスト11 nameと階層構造
<view name="A">
  <view name="a"> (1)
    <view name="b"> (2)
      <view name="c">  (3)
      </view>
      <view name="d">  (4)
      </view>
    </view>
  </view>
</view>
<view name="B">
  <view name="a">
    <view name="b">
      <view name="c"> (5)
      </view>
    </view>
  </view>
</view>

リスト12 nameの例
<canvas proxied="false" bgcolor="0xffffcc"> 
  <window title="ウインドウ2" name="win2" y="150" width="200" height="100">
    <method name="moveRight">//メソッド1
      this.animate('x',20,300,true);
    </method>
  </window>
  <window title="ウインドウ1" width="300">
    <method name="moveRight">//メソッド2
      this.animate('x',20,300,true);
    </method>
    <simplelayout spacing="4"/>
    <button text="(1)ウインドウ1を動かす" onclick="parent.moveRight()"/>
    <button text="(2)ウインドウ2を動かす" onclick="parent.parent.win2.moveRight()"/>
    <button text="(3)このボタンを動かす" onclick="this.moveRight()">
      <method name="moveRight">//メソッド3
        this.animate('x',20,300,true);
      </method>
    </button>
  </window>
</canvas>

リスト12サンプル

リスト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
リスト13 idと階層構造
<view id="A">
  <view id="a"> (1)
    <view id="b"> (2)
      <view id="c">  (3)
      </view>
      <view id="d">  (4)
      </view>
    </view>
  </view>
</view>
<view id="B">
  <view id="e">
    <view id="f">
      <view id="g"> (5)
      </view>
    </view>
  </view>
</view>

リスト14はリスト12の相対指定の部分をid指定に変更したものです。リスト12との変更点は2ヶ所で、2行目のウインドウ2のnameをidに、13行目のボタン(2)の相対指定(parent.parent.win2.moveRight())を絶対指定(win2.moveRight())に変えています。

リスト14 idの例
<canvas proxied="false" bgcolor="0xffffcc"> 
  <window title="ウインドウ2" id="win2" y="150" width="200" height="100">
    <method name="moveRight">//メソッド1
      this.animate('x',20,300,true);
    </method>
  </window>
  <window title="ウインドウ1" width="300">
    <method name="moveRight">//メソッド2
      this.animate('x',20,300,true);
    </method>
    <simplelayout spacing="4"/>
    <button text="(1)ウインドウ1を動かす" onclick="parent.moveRight()"/>
    <button text="(2)ウインドウ2を動かす" onclick="win2.moveRight()"/>
    <button text="(3)このボタンを動かす" onclick="this.moveRight()">
      <method name="moveRight">//メソッド3
        this.animate('x',20,300,true);
      </method>
    </button>
  </window>
</canvas>

リスト14サンプル

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があれば親タグではなく指定されたタグのイベントに反応します。

リスト15 setAttribute()とイベントハンドラ
<canvas proxied="false" bgcolor="0xffffcc">
  <button id="btn">
    <handler name="onclick">
      this.setAttribute('x',100); // (1)
    </handler>
  </button>
  <view id="v1" y="30" width="20" height="20" bgcolor="red">
    <handler name="onx" reference="btn">  // (2)
      this.setAttribute('x',20);
      this.setAttribute('y',50);
    </handler>
  </view>
  <view id="v2" y="50" width="20" height="20" bgcolor="yellow">
    <handler name="onx" reference="v1">  // (3)
      this.setAttribute('x',40);
      this.setAttribute('y',50);
    </handler>
  </view>
  <view id="v3" y="70" width="20" height="20" bgcolor="blue">
    <handler name="ony" reference="v2">  // (4)
      this.setAttribute('x',60);
      this.setAttribute('y',50);
    </handler>
  </view>
</canvas>

リスト15サンプル

(1)・ボタンクリック時にボタンのxを100に変更(=右に移動する)。→ ボタンbtnにonxイベントが発生する。

(2)・ボタンbtnのonxイベントを受信して実行。→ ビューv1にonxイベント、onyイベントが発生する。

(3)・ビューv1のonxイベントを受信して実行。→ ビューv2にonxイベント、onyイベントが発生する。

(4)・ビューv2のonyイベントを受信して実行。→ ビューv3にonxイベント、onyイベントが発生するがそれを受信するイベントハンドラはどこにもないので無視される。

おすすめ記事

記事・ニュース一覧