click绑定

目的

click绑定在DOM元素上添加事件句柄以便元素被点击的时候执行定义的JavaScript 函数。大部分是用在button,input和连接a上,但是可以在任意元素上使用。

例子

<div>
    You've clicked
    <span data-bind="text: numberOfClicks">
    </span>
    times
    <button data-bind="click: incrementClickCounter">
        Click me
    </button>
</div>
<script type="text/javascript">
    var viewModel = {
        numberOfClicks: ko.observable(0),
        incrementClickCounter: function() {
            var previousCount = this.numberOfClicks();
            this.numberOfClicks(previousCount + 1);
        }
    };
</script>

每次点击按钮的时候,都会调用incrementClickCounter()函数,然后更新自动更新点击次数。

参数

主参数

Click点击事件时所执行的函数。

你可以声明任何JavaScript函数 – 不一定非要是view model里的函数。你可以声明任意对象上的任何函数,例如:someObject.someFunction。

View model上的函数在用的时候有一点点特殊,就是不需要引用对象的,直接引用函数本身就行了,比如直接写incrementClickCounter就可以了,而无需写成:viewModel.incrementClickCounter(尽管是合法的)。

其它参数

注1:传参数给你的click 句柄

最简单的办法是传一个function包装的匿名函数:

<button data-bind="click: function() { viewModel.myFunction('param1', 'param2') }">
    Click me
</button>

这样,KO就会调用这个匿名函数,里面会执行viewModel.myFunction(),并且传进了'param1' 和'param2'参数。

注2:访问事件源对象

有些情况,你可能需要使用事件源对象,Knockout会将这个对象传递到你函数的第一个参数:

<button data-bind="click: myFunction">
    Click me
</button>
<script type="text/javascript">
    var viewModel = {
        myFunction: function(event) {
            if (event.shiftKey) { //do something different when user has shift key down             } else {                 //do normal action             }         }     };
                
</script>

如果你需要的话,可以使用匿名函数的第一个参数传进去,然后在里面调用:

<button data-bind="click: function(event) { viewModel.myFunction(event, 'param1', 'param2') }">
    Click me
</button>

这样,KO就会将事件源对象传递给你的函数并且使用了。

注3: 允许执行默认事件

默认情况下,Knockout会阻止冒泡,防止默认的事件继续执行。例如,如果你点击一个a连接,在执行完自定义事件时它不会连接到href地址。这特别有用是因为你的自定义事件主要就是操作你的view model,而不是连接到另外一个页面。

当然,如果你想让默认的事件继续执行,你可以在你click的自定义函数里返回true。

注4:控制this句柄

初学者可以忽略这小节,因为大部分都用不着,高级用户可以参考如下内容:

KO在调用你定义的函数时,会将view model传给this对象(也就是ko.applyBindings使用的view model)。主要是方便你在调用你在view model里定义的方法的时候可以很容易再调用view model里定义的其它属性。例如: this.someOtherViewModelProperty。

如果你想引用其它对象,我们有两种方式:

  • 你可以和注1里那样使用匿名函数,因为它支持任意JavaScript 对象。

  • 你也可以直接引用任何函数对象。你可以使用bind使callback函数设置this为任何你选择的对象。例如:

<button data-bind="click: someObject.someFunction.bind(someObject)">
    Click me
</button>

如果你是C#或Java开发人员,你可以疑惑为什么我们还要用bind函数到一个对象想,特别是像调用someObject.someFunction。 原因是在JavaScript里,函数自己不是类的一部分,他们在单独存在的对象,有可能多个对象都引用同样的someFunction函数,所以当这个函数被调用的时候它不知道谁调用的(设置this给谁)。在你bind之前运行时是不会知道的。KO默认情况下设置this对象是view model,但你可以用bind语法重定义它。

在注1里使用匿名函数的时候没有具体的要求,因为JavaScript代码someObject.someFunction()就意味着调用someFunction,然后设置this到 someObject对象上。

注5:防止事件冒泡

默认情况下,Knockout允许click事件继续在更高一层的事件句柄上冒泡执行。例如,如果你的元素和父元素都绑定了click事件,那当你点击该元素的时候两个事件都会触发的。如果需要,你可以通过额外的绑定clickBubble来禁止冒泡。例如:

<div data-bind="click: myDivHandler">
    <button data-bind="click: myButtonHandler, clickBubble: false">
        Click me
    </button>
</div>

默认情况下,myButtonHandler会先执行,然后会冒泡执行myDivHandler。但一旦你设置了clickBubble为false的时候,冒泡事件会被禁止。

依赖性

除KO核心类库外,无依赖。

使用click binding可以对某一个可见的DOM元素进行事件绑定,当用户点击这个元素时会执行对应的方法,完成相应的功能,我们经常在button、input、a等DOM元素上进行click binding。

(1)、下面我们来看一个例子:每当我点击Button之后,点击次数都会加1

<script type="text/javascript" src="knockout-2.2.0.js">
</script>
<div>
    You've clicked
    <span data-bind="text: numberOfClicks">
    </span>
    times
    <button data-bind="click: incrementClickCounter">
        Click me
    </button>
</div>
<script type="text/javascript">
    var viewModel = {
        numberOfClicks: ko.observable(0),
        incrementClickCounter: function() {
            var previousCount = this.numberOfClicks();
            this.numberOfClicks(previousCount + 1);
        }
    };
    ko.applyBindings(viewModel);
</script>

(2)、我们也可以把当前元素作为参数传递给所要调用的方法,如下例子:我们点击按钮,然后删除按钮对应的元素

<script type="text/javascript" src="knockout-2.2.0.js">
</script>
<ul data-bind="foreach: places">
    <li>
        <span data-bind="text: $data">
        </span>
        <button data-bind="click: $parent.removePlace">
            Remove
        </button>
    </li>
</ul>
<script type="text/javascript">
    function MyViewModel() {
        var self = this;
        self.places = ko.observableArray(['London', 'Paris', 'Tokyo']);

        // The current item will be passed as the first parameter, so we know which place to remove 
        self.removePlace = function(place) {
            self.places.remove(place)
        }
    }
    ko.applyBindings(new MyViewModel());
</script>

关于这个例子我们有以下的两点说明:

(a)、如果我们在一个嵌套的元素中使用click binding的话,例如我们在foreach或者with块中使用click binding,但是此时click对应的方法在View Model的根部或者他的父上下文,此时我们需要一个前缀比如$parent或者$root来定位我们的方法。

(b)、在我们的View Model中我们经常为我们的this起一个别名为self,这样就会防止this指向的地方改变而导致我们程序的错误。

(3)、定义事件对象和传递更多的参数

(a)、定义一个事件

<button data-bind="click: myFunction">
    Click me
</button>
<script type="text/javascript">
    var viewModel = {
        myFunction: function(data, event) {
            if (event.shiftKey) {
                //do something different when user has shift key down 
            } else {
                //do normal action 
            }
        }
    };
    ko.applyBindings(viewModel);
</script>

此时就会判断,如果是shift 键点击的话,则会执行对应的内容,否则执行其他的内容。

(b)、传递更多的参数

如果我们想要传递很多参数给调用的方法的话,我们可以在标签中包括对应方法要传递的参数等,如下:

<button data-bind="click: function(data, event) { myFunction('param1', 'param2', data, event) }">
    Click me
</button>

但是,如果我们不想要在标签中出现这么多的内容的话,我们可以使用KO的bind方法来进行方法的传递,如下:

<button data-bind="click: myFunction.bind($data, 'param1', 'param2')">
    Click me
</button>

(4)、防止事件的冲突

在默认情况下,Knockoutjs允许事件绑定从一个元素传递到更高一级的元素。比如:一个DOM元素和他的父元素都使用了click事件,如果我们点击其中一个则两个事件都会执行,此时,我们就可以使用“clickBubble”来制定哪个事件不执行,如下:

<div data-bind="click: myDivHandler">
    <button data-bind="click: myButtonHandler, clickBubble: false">
        Click me
    </button>
</div>

在默认的情况下,当点击事件时会首先执行myButtonHandler,然后执行myDivHandler。但是当我们在button上使用的clickBubble:false时,则会跳过myButtonHandler去执行myDivHandler了。