Knockout.js似乎正在打破我的jQuery事件处理程序,多么粗鲁

好吧,我一直试图解开这个混乱几个小时,并且无处可逃,类似于追逐尾巴的狗。 这是情况。

我为我的UI使用Knockout.js,它本身很好用。 但是,我试图使用一些第三方代码,使下拉菜单和checkbox看起来都很漂亮。 其实我甚至不确定这是第三方库还是我们的devise师写的。 这段代码隐藏了真正的checkbox,并用一个假的<span />替代它,通过CSS来模仿一个checkbox。 跨度的click事件触发真实checkbox的change事件:

 // this code updates the fake UI this._changeEvent = function() { self.isChecked = self.$input.is(':checked'); self._updateHTML(false, true); jQuery(self).trigger('change'); }; // when the user clicks the fake checkbox, we trigger change on the real checkbox this.$fake.on('click', function(e) { e.preventDefault(); self.$input.click().trigger('change'); }); // Bind _changeEvent to the real checkbox this.$input.change(this._changeEvent); 

这实际上与Knockout.js一起工作,因为Knockout将监听该事件处理程序。 换句话说,当用户点击假checkbox时,绑定的淘汰模型将被更新。 但是,不起作用的是更新模型。 如果我打电话:

 model.SomeValue(!curValue); // SomeValue is bound to a checkbox, flip its value 

该模型得到更新,但假的用户界面不更新。 我已经将这个问题追溯到ko.bindingHandlers.checked.update中的代码,它执行以下操作:

 // When bound to anything other value (not an array), the checkbox being checked represents the value being trueish element.checked = value; 

基本上, element.checked属性设置,但没有事件被激发。 因此, _changeEvent函数永远不会被调用。 所以,我已经实现了我自己的ko.bindingHandlers.checked.update函数,它是内置函数的一个副本。 从理论上讲,这是我应该做的一切:

  ko.bindingHandlers.checked.update = function (element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()); if (element.type == "checkbox") { if (value instanceof Array) { // When bound to an array, the checkbox being checked represents its value being present in that array element.checked = ko.utils.arrayIndexOf(value, element.value) >= 0; } else { // When bound to anything other value (not an array), the checkbox being checked represents the value being trueish //element.checked = value; $(element).prop('checked', value).trigger('change'); // <--- this should work! } } else if (element.type == "radio") { element.checked = (element.value == value); } }; 

而不是设置element.checked ,而是调用.prop('checked', value)并触发change事件。 但是,这是行不通的。 以下是我所知道的:

  1. 如果我从等式中删除Knockout.js, $(element).prop('checked', value).trigger('change'); 工作得很好。 所以,knockout.js是如何与事件搞砸。 是解除绑定该事件处理程序?
  2. 我已经确认$(element)是一样的this.$input假checkbox绑定代码。 我可以在这个元素上设置其他的expando属性,然后显示出来。
  3. 我尝试了一些方法来尝试debugging到Knockout.js和jQuery来查看事件是否仍然绑定,但是我并没有真正得到任何地方这种方法。 我的直觉是,Knockout.js以某种方式取代了自己的内部change事件处理程序,现有的绑定被删除。 我还没有find一种方法来确认这一点呢。

我的问题:主要是,我正在寻找解决这个问题的方法。 Knockout.js是否删除模型应用之前存在的现有change事件? debugging这个代码的下一个步骤是什么,并确切地确定发生了什么?

而不是重写基本检查的处理程序,只需创build自己的自定义绑定来更新UI。

以下是处理更新UI的模型和自定义绑定的示例:

 var Model = function () { this.checked = ko.observable(false); }; ko.bindingHandlers.customCheckbox = { init: function (element) { // Create the custom checkbox here $(element).customInput(); }, update: function (element) { // Update the checkbox UI after the value in the model changes $(element).trigger('updateState'); } }; 

我将把模型绑定到下面的HTML:

 <input type="checkbox" name="genre" id="check-1" value="action" data-bind="checked: checked, customCheckbox: checked" /> 

而这真的就是我需要做的。

这是一个例子: http : //jsfiddle.net/badsyntax/4Cy3y/

– 我想我是通过阅读你的问题而冲过来的,没有得到你所问的关键。 无论如何,我会在这里留下我的答案。

尝试触发并聆听自定义事件:

 element.checked = value; element.dispatchEvent(new Event("forcedChange")); 

尝试创buildcheckbox对象作为一个jQuery插件,并创build敲除自定义绑定绑定到您的视图模型。 我已经build立了一个jsfiddle,只是这样做: http : //jsfiddle.net/markhagers/zee4z/3/

 $(function () { var viewModel = { truthyValue1: ko.observable(false), truthyValue2: ko.observable(true) }; ko.applyBindings(viewModel); }); 

(我不得不添加这个片段,或者我的答案没有被接受,请参阅完整代码示例的jsfiddle)。 对不起,它看起来像很多代码,但插件部分原本是一个独立的jquery插件,随后添加淘汰赛的绑定,其中的一些代码已淘汰的绑定。 它可以放在它自己的文件,以减less混乱。 创build自定义绑定logging在淘汰赛网站上。

只是头脑风暴,但是这个使用事件命名空间开发的第三方控件? 这可能就像你build议的那样,淘汰赛和这个控制是以某种方式与默认的“变化”事件作斗争。 在开发小部件和应用程序时,我总是在使用jQuery绑定事件时使用事件命名空间。

使用“myWidget”命名空间的示例:

 $('#element').on('change.myWidget', function(e) { // handle the event } 

虽然我可以完全脱离基地。

只是一个快速的想法。 您的devise师似乎触发了此行中的点击和更改事件:

 self.$input.click().trigger('change'); 

难道这不是可以尝试的吗? 也许它是绑定到点击事件,而不是更改事件? 在你的其他语句中试试这个:

 $(element).prop('checked', value).click().trigger('change'); // <--- this should work! 

我已经理解了.click()的作用:它模仿鼠标点击,同时触发点击事件(Duh?)。

我知道有时候(比如在收音机或checkbox中),一些编码人员将事件绑定到点击而不是更改,因为这更有可能在正确的时间触发(在IE7中,更改事件以非常奇怪的方式触发)。

只是一个想法,可能是一些尝试。 希望能帮助到你!

编辑:我看到你的事件和自定义事件中的命名空间的答案。 这也可以帮助你,希望这不是一个太大的变化。

/ J。

这听起来像你所遇到的问题不是由于从knockout和另一组代码(例如,更改checkbox的CSS)事件的冲突,而是由于JavaScript线程和事件的冲突线程,处理。

Javascript在浏览器中作为一个单独的线程处理:所有的进程顺序运行,任何中断顺序的事件都可能导致事件失败。 如果您可以独立调用每个函数,但是将两者都包含在脚本中时会产生干扰,触发checkbox的CSS更改的onclick或onmousedown事件可能会干扰挖掘脚本。 在这个“单线程”中,所有事件都会影响另一个甚至是鼠标移动的执行。

这个( http://ejohn.org/blog/how-javascript-timers-work/ )例子用定时器来演示这个想法,但我认为你目前的情况可能会遭受同样的问题。

为了解决这个问题,你可以写一个简短的函数,例如:

函数doBoth(checkboxid){

$( “#checkboxid”)addclass( “prettychecked”)。

$(“#checkboxid”)。prop('checked',true);

}

重要的一点是,每个动作都是按顺序进行的,而不会中断鼠标事件。

希望这可以帮助!