Mach3.laBlog

細かすぎて伝わらないjQuery拡張 (7) “$.eventify” – Advent Calendar 2016

この記事は賞味期限切れです。(更新から1年が経過しています)

$.eventify() は、オブジェクトに on, off, trigger といったイベント管理のメソッドをぶら下げるための関数です。

$.eventify(obj)

使い方

var obj = {
    onTest: function(){
        console.log("test");
    }
};

$.eventify(obj);

obj.on("test", obj.onTest);
obj.trigger("test"); // => "test"
obj.off("test", obj.onTest);
obj.trigger("test"); // 何も起きない

jQuery のイベントと使い方は全く同じです。 また、jQuery 同様に trigger でハンドラーにデータを渡すこともできます。

var obj = {
    onTest: function(e, data){
        console.log(data);
    }
};

$.eventify(obj);

obj.on("test", obj.onTest);
obj.trigger("test", "Hello, World"); // => "Hello, World"

コード

$.eventify = function(obj){
    var emitter = $(obj);
    ["on", "off", "trigger"].forEach(function(name){
        obj[name] = function(){
            emitter[name].apply(emitter, arguments);
            return this;
        };
    });
    return obj;
};

jQuery の on, off, trigger をラッピングしてぶら下げているだけです。 本当は自分で実装した方が良い気はちょっとしています。

気になっている点

というのも、使っていてちょっと気になる挙動がありました。 「イベントと同じ名前のメソッドを持っていると、triggerした時点で実行されてしまう」 という物です。 ちなみに、そういったケースでは trigger でデータを渡すことは出来ない様です。Eventオブジェクトも渡されません。

var obj = $.eventify({
    test: function(){
        console.log("ハンドラを登録していなくても実行されちゃう");
    }
});
obj.trigger("test"); // "ハンドラを登録していなくても実行されちゃう"

一方、dispatchEvent() を使用した場合は、addEventListener で登録していなければ実行される事はありません。 (ただし、onclick 等予約されている属性については実行されます。click では実行されません。) customEvent() は対応環境もまだ多くないので実験は割愛しました。

var el = document.createElement("div");
el.test = function(){
    console.log("これは呼ばれません");
};
el.ontest = function(){
    console.log("これも呼ばれません");
};
el.dispatchEvent(new Event("test"));

jQuery のコードを読んでみましたが、”test” をトリガーした時に、 発火元のオブジェクトに “ontest” あるいは “test” というメソッドがある場合そのいずれかをコールする仕組みになっている様でした。 (jQuery は jQueryEvent として独自実装しているので、読むのもなかなか骨が折れます)

こういう物だと思っていればこれはこれで使い道があるかもしれません。 どうしても気になるようなら自前で実装するか、node.js の EventEmitter を browserify 等で活用すると良いかもしれません。

コード(自前の実装)

自前で簡易実装してみた $.eventify() です。

$.eventify = function(obj){
    return (function(){
        var hasAsArray = function(key, obj){
            return (key in obj) && $.isArray(obj[key]);
        };
        this._listeners = {};
        this.on = function(type, handler){
            if(! hasAsArray(type, this._listeners)){
                this._listeners[type] = [];
            }
            this._listeners[type].push(handler);
        };
        this.off = function(type, handler){
            if(hasAsArray(type, this._listeners)){
                this._listeners[type] = this._listeners[type]
                .filter(function(_handler){
                    return (handler !== _handler);
                });
            }
        };
        this.trigger = function(type, data){
            var self = this;
            if(hasAsArray(type, this._listeners)){
                this._listeners[type].forEach(function(handler){
                    handler.apply(self, [new Event(type), data]);
                });
            }
        };
        return this;
    }).call(obj);
};

new Event() ではなく new CustomEvent() の方が良いかもしれませんが、 CustomEvent はIEでは非対応になっています。 CustomEvent で使う際には下記URLにあるPolyfillを使うと良いかもしれません。

コメント

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*