Closure Library 超入門 〜イベント編〜

はじめに

Closure Library に少しずつ慣れてきたので、自分が覚えたことをブログに整理してみます。Closure Library は機能が多すぎるので、私が使い方を覚えた順番で書いていく予定。

まずはイベントから。

Closure Library のイベントの基本

イベント機能は goog.events 名前空間で提供されます。最低限覚えておく基本は次の通り。

Closure Library のイベント機能では、DOM が発生させるイベントと、ウィジェットが発生させるイベントを、同じ方法でイベントを処理できます。

<html>
    <head>
        <title>goog.events</title>
        <link rel="stylesheet" type="text/css" href="closure-library/closure/goog/css/common.css"/>
        <link rel="stylesheet" type="text/css" href="closure-library/closure/goog/css/button.css"/>
    </head>
    <body>
        <input id="button1" type="button" value="Hello"/>
        <div id="button2"></div>

        <script type="text/javascript" src="closure-library/closure/goog/base.js">
        </script>
        <script type="text/javascript">
            goog.require("goog.dom");
            goog.require("goog.events");
            goog.require("goog.ui.Button");
        </script>
        <script type="text/javascript">
            /**
             * DOM のとき
             */

            function onClick(e) {
                alert("Hello");

                // 解除
                goog.events.unlisten(
                    goog.dom.getElement("button1"),
                    goog.events.EventType.CLICK,
                    onClick);
            };

            // 登録
            goog.events.listen(
                goog.dom.getElement("button1"),
                goog.events.EventType.CLICK,
                onClick);
        </script>
        <script type="text/javascript">
            /**
             * ウィジェットのとき
             */

            // Button ウィジェット表示
            var button = new goog.ui.Button("Hi");
            button.render(goog.dom.getElement("button2"));

            function onAction(e) {
                alert("Hi");

                // 解除
                goog.events.unlisten(
                    button,
                    goog.ui.Component.EventType.ACTION,
                    onAction);
            };

            // 登録
            goog.events.listen(
                button,
                goog.ui.Component.EventType.ACTION,
                onAction);
        </script>
    </body>
</html>

EventHandler

いちいち unlisten を呼んでイベントハンドラを解除するのが面倒な人向け。上記の goog.events.listen や goog.events.unlisten をラップしていて、同名のメソッドを提供しています。ただ、クラス名がおかしいですよね。EventManager とかにするべき。

EventHandler インスタンスが破棄されるとき、インスタンスを使って登録したハンドラも解除される、というのが特徴。

<html>
    <head>
        <title>goog.events.EventHandler</title>
        <link rel="stylesheet" type="text/css" href="closure-library/closure/goog/css/common.css"/>
        <link rel="stylesheet" type="text/css" href="closure-library/closure/goog/css/button.css"/>
    </head>
    <body>
        <div id="button1"></div>

        <script type="text/javascript" src="closure-library/closure/goog/base.js">
        </script>
        <script type="text/javascript">
            goog.require("goog.dom");
            goog.require("goog.events");
            goog.require("goog.ui.Button");
        </script>
        <script type="text/javascript">
            var eventHandler = new goog.events.EventHandler();

            // Button ウィジェット表示
            var button = new goog.ui.Button("Hi");
            button.render(goog.dom.getElement("button2"));

            function onAction(e) {
                alert("Hi");

                // 解除
                eventHandler.unlisten(
                    button,
                    goog.ui.Component.EventType.ACTION,
                    onAction);
            };

            // 登録
            eventHandler.listen(
                button,
                goog.ui.Component.EventType.ACTION,
                onAction);
        </script>
    </body>
</html>

Closure Library のイベントはバブルイベント

というか、DOM のイベントと同じ。親要素に伝搬される。つまり、親要素に登録したイベントハンドラで、子要素が発生させたイベントを捕捉できます。

最初のころ、直接ハンドラを登録してないのに呼び出されて戸惑ったのでメモ。

<html>
    <head>
        <title>BubbleEventSample</title>
        <link rel="stylesheet" type="text/css" href="closure-library/closure/goog/css/common.css"/>
        <link rel="stylesheet" type="text/css" href="closure-library/closure/goog/css/toolbar.css"/>
    </head>
    <body>
        <div id="toolbar" class="goog-toolbar">
        </div>

        <script type="text/javascript" src="closure-library/closure/goog/base.js">
        </script>
        <script type="text/javascript">
            goog.require("goog.dom");
            goog.require("goog.events");
            goog.require("goog.ui.Toolbar");
            goog.require("goog.ui.ToolbarButton");
        </script>
        <script type="text/javascript">
            // Toolbar ウィジェット表示
            var toolbar = new goog.ui.Toolbar();
            toolbar.decorate(goog.dom.getElement("toolbar"));
            var button = new goog.ui.ToolbarButton("Hello");
            toolbar.addChild(button, true);

            // ToolbarButton が発生させる ACTION イベントを
            // Toolbar に登録したイベントハンドラで処理できる
            function onAction(e) {
                alert("Hello");
            };

            // 登録
            goog.events.listen(
                toolbar,
                goog.ui.Component.EventType.ACTION,
                onAction);
        </script>
    </body>
</html>

カスタムイベント

用意されたイベントを使うだけでなく、自分でイベントを作成することも可能。カスタムイベントの作成には goog.events 名前空間で提供されている Event クラスと EventTarget クラスを使います。

Event クラスは、イベントデータを格納するクラスのベース。EventTarget クラスは、イベントを発生させるクラスのベース。…EventTarget って名前も悪いですね。イベントを発生させるんだから EventSource とかの方がいい。

作成したカスタムイベントは、DOM やウィジェットが発生させるイベントと同じようにリッスンできます。

<html>
    <head>
        <title>goog.events.CustomEvent</title>
    </head>
    <body>
        <script type="text/javascript" src="closure-library/closure/goog/base.js">
        </script>
        <script type="text/javascript">
            goog.require("goog.dom");
            goog.require("goog.events");
            goog.require("goog.events.EventTarget");
        </script>
        <script type="text/javascript">
            goog.provide("sample.GreetEvent");
            goog.provide("sample.Greeter");
            goog.provide("sample.Greeter.EventType");

            // イベントのデータを格納するクラス
            sample.GreetEvent = function(type, name) {
                goog.base(this, type);
                this.messege_ = "Hello, " + name;
            };
            goog.inherits(sample.GreetEvent, goog.events.Event);
            sample.GreetEvent.prototype.messege_;
            sample.GreetEvent.prototype.getMessage = function() {
                return this.messege_;
            };

            // イベントを発生させるクラス
            sample.Greeter = function() {
                goog.base(this);
            };
            goog.inherits(sample.Greeter, goog.events.EventTarget);
            sample.Greeter.prototype.greet = function(name) {
                // イベントを発生させる
                var e = new sample.GreetEvent(
                    sample.Greeter.EventType.GREET, name);
                this.dispatchEvent(e);
            };
            
            // イベントの種類
            sample.Greeter.EventType = {
                GREET: "greet"
            };
        </script>
        <script type="text/javascript">
            // イベントをリッスンする
            var greeter = new sample.Greeter();
            goog.events.listen(greeter,
                sample.Greeter.EventType.GREET,
                function(e) {
                    alert(e.getMessage());
                });
            greeter.greet("Naruto");
        </script>
    </body>
</html>

ウィジェットの一番基本となる Component クラスは EventTarget を継承しています。ウィジェット作るなら、わざわざ継承してくてもいいです。

まとめ

Closure Library のインフラに乗っかれば、簡単にイベントが作れるし使えます。クラス名が変なんで最初混乱しますけどね。