LoginSignup
4
4

More than 5 years have passed since last update.

【knockout.js】 要素の外側のクリックにバインド 【カスタムバインディング】

Last updated at Posted at 2013-06-01

以前公開した jQuery プラグインの「skOuterClick」がなかなか需要があるようなので、outerClick の Knockout 版カスタムバインディングを公開します。

カスタムバインディング本体

これを knockout.js と jQuery の後に読み込みます。

ko.outerClick.js
/**
 * ko.outerClick.js
 * depend on jQuery
 */

(function() {
    var dummyCallback = function(){};
    var uo = ko.utils.unwrapObservable; // alias
    function searchNearestElements(selector, startingElement) {
        var candidates = startingElement.closest(selector);
        if (candidates.length > 0) return candidates;
        candidates = $(selector);
        if (candidates.length > 0) return candidates;
        return false;
    };

    /**
     * Custom binding [ outerClick ]
     * 要素の外側のクリックイベントに対してコマンドをバインドする
     *
     * binding-type: function/object
     * binding-object: {
     *  command:    [default:function] 要素の外側がクリックされた際に呼び出されるコールバック関数を指定する。
     *  enable:     true = commandの実行を可能にする。動的バインド可能。
     *              デフォルトは true
     *  inners:     内側として扱う要素をCSSセレクタで指定する。
     *              要素はまず近い親要素から検索され、見つからなければDOM全体から検索される。
     *              デフォルトは false(指定なし)
     *
     * }
     */
    ko.bindingHandlers['outerClick'] = {
        init: function(element, valueAccessor) {
            var value = uo(valueAccessor());
            var elm = $(element);
            var config = ko.utils.extend({
                command: dummyCallback,
                enable: true,
                inners: false
            }, (typeof value == 'function' ? {command: value} : value));
            var isInner = false;
            // Bind click event to suppress
            function onInnerClick(e){
                isInner = true;
            };
            elm.click(onInnerClick);
            if (config.inners) {
                var inners = searchNearestElements(config.inners, elm);
                if (inners.length > 0) inners.each(function(inner) {
                    $(inner).click(onInnerClick);
                });
            }
            // Bind click elsewhere
            $(document).click(function(e) {
                if (isInner) {
                    isInner = false;
                    return;
                }
                if (uo(config.enable)) config.command.call(element, ko.dataFor(element), e);
            });
        }
    };
})();

使い方

view_1.html
<div data-bind="outerClick: showMessage">
    ここをクリックしてもなにも起きません。<br />
    この外側をクリックするとメッセージが表示されます。
</div>
view-model_1.js
function AppViewModel() {
    this.showMessage = function() {
        alert("メッセージ")
    }
}
$(function() {
    ko.applyBindings(new AppViewModel());
}

使い方(アドバンスト)

view_2.html
<div data-bind="outerClick: { command: showMessage,
                              enable: showMessageExecutable,
                              inners: '.inner_box' }">
    ここをクリックしてもなにも起きません。<br />
    この外側をクリックするとメッセージが表示されます。
</div>
<div class="inner_box">
    さらにここをクリックしてもなにも起きません。
    なぜならここも inners 指定によって「内側」として扱われるからです。
</div>
view-model_2.js
function AppViewModel() {
    this.showMessage = function() {
        alert("メッセージ")
    }
    this.showMessageExecutable = ko.observable(true);
}
$(function() {
    var viewModel = new AppViewModel()
    ko.applyBindings(viewModel);
    // ...later
    viewModel.showMessageExecutable(false); // これでメッセージは表示されなくなる
}
4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4