The Document of Aska

2-2. CustomEvent

clickやsubmitといったDOMのイベントの他にあるアプリケーションレベルのイベントです。

私は自分で実装していますが、各種Jsライブラリで賄うことも出来ます。

実装例

app.addEvents = function(){
    var args = $.makeArray(arguments);
    for (var i = 0, max_i = args.length; i < max_i; i++) {
        app.events[args[i]] = [];
    }
}
app.addListener = function(name, fh, context){
    if (!(name in app.events)) {
        console.log('unknown event ' + name);
    }
    app.events[name].push([fh, context]);
}
app.fireEvent = function(){
    var args = $.makeArray(arguments);
    var name = args.shift();
    if (!(name in app.events)) {
        console.log('unknown event ' + name);
        return;
    }
    for (var i = 0, max_i = app.events[name].length; i < max_i; i++) {
        app.events[name][i][0].apply(app.events[name][i][1] || app, args);
    }
}

効果

この機構はアプリケーション内で発生するイベントを視覚化、イベント処理を抽象化し、イベントの発生元と影響先間の結合を弱くしDOM構造の変更つまり画面レイアウトの変更に強くします。

7kai Tasksではイベントを駆使し、PC版とスマホ版の仕様・コードをなるべく共通化しています。

メソッド

  • app.addEvents(name) ... イベント定義(こういうイベントがあります)
  • app.addListener(name, function) ... イベント処理定義(このイベントが発火したらこのfunctionをcall)
  • app.fireEvent(name, args) ... イベント発火(このイベントを発火)

イベント定義: app.addEvents(name)

  • スコープ: 最外層かつ上部

そのアプリにどんなイベントがあるのか分かりやすくする為、上部に固めて宣言します。

宣言する前のイベントに対して app.addListenerapp.fireEvent は出来ません。

Code

app.addEvents('setup');        // application setup
app.addEvents('clear');        // memory and dom clear
app.addEvents('reload');       // clear => setup
app.addEvents('resize');       // window resize
app.addEvents('alert');        // trouble
app.addEvents('selectTab');    // tag component
app.addEvents('receiveSign');  // receive sign from api (signin)
app.addEvents('receiveToken'); // receive token from api (online)

イベント処理定義: app.addListener(name, function)

  • スコープ: 最外層, app.setup.*

Code

// アプリケーションにリストが登録された場合、メモリに格納
app.addListener('registerList', function(list){
    app.data.list_map[list.id] = list;
});

// リストが選択された場合、DOM書き換え
// `data-setup="listname"` と初期化処理が指定されているDOM全てのtextが書き換わる
app.setup.listname = function(ele){
    app.addListener('openList', function(list){
        ele.text(list.name);
    });
}

イベント発火: app.fireEvent(name)

  • スコープ: どこでも

「リストを開く」というイベントの場合

  1. メニューからクリック
  2. 画面初期化時に前回開いていたリストをlocalStorageから取得
  3. デスクトップ通知をクリック
  4. ショートカットキー操作

という4通りの発火元がありますが、リストを開いた処理については発火元周辺のコードで一切関知しない為、大変書き易いです。

Code

// リストのメニューをクリックしたら対応するリストを開くイベントを発火
app.setup.listMenu = function(li){
    var id = li.data('id');
    var list = app.data.list_map[id];
    app.fireEvent('openList', list);
};

// リストが開かれたらタスクの表示条件を更新
app.addListener('openList', function(list){
    app.fireEvent('filterTask', { list_id: list.id });
});