JavaScriptでイベント機能を追加するmixin

by ginpei

HTML

<p><span id="timer"></span>ms</p>

JavaScript

/**
 * イベントまわりの処理。
 * @mixin
 * @sample
 * var obj = _.extend({}, _eventer, {
 *   // …
 * });
 *
 * obj.on('foo', function(a, b) {
 *   console.log(a, b, this.c);  // 'A', true, 123
 * }, { c:123 });
 * obj.trigger('A', true);
 */
var _eventer = {
    _listeners: null,

    /**
     * イベントを発火させる。
     * @param {String} type
     */
    trigger: function(type /* , ... */) {
        if (this._listeners && type in this._listeners) {
            var args = Array.prototype.slice.call(arguments, 1);
            var listeners = this._listeners[type];
            for (var i=0, l=listeners.length; i<l; i++) {
                listeners[i].apply(this, args);
            }
        }
    },

    /**
     * イベントを監視する。
     * @param {String} type
     * @param {Function} listeners
     * @param {Object} [context]
     */
    on: function(type, listener, context) {
        if (context) {
            listener = listener.bind(context);
        }

        if (!this._listeners) {
            this._listeners = {};
        }

        if (!(type in this._listeners)) {
            this._listeners[type] = [];
        }

        this._listeners[type].push(listener);
    }
};

// --------------------------------
// Example:


// イベント処理を持つオブジェクト
var timer = Object.create(_eventer);
timer.startedAt = Date.now();
timer.el = document.getElementById('timer');
timer.update = function() {
    var elTimer = this.el;
    if (elTimer.firstChild) {
        elTimer.removeChild(elTimer.firstChild);
    }
    elTimer.appendChild(document.createTextNode(Date.now()-this.startedAt));
};

// イベント発火時の処理
timer.on('changetime', timer.update, timer);

// 定期的にイベント発火
setInterval(function() {
    timer.trigger('changetime');
}, 1);