36

好的,我已经尝试解开这个烂摊子几个小时了,但一无所获,类似于狗追逐它的尾巴。这是情况。

我在我的 UI 中使用了 Knockout.js,它本身就很好用。但是,我正在尝试使用一些第三方代码,使下拉菜单和复选框看起来都很漂亮。实际上,我什至不确定这是第三方库还是我们的设计师写的东西。此代码隐藏了真正的复选框,并将其替换为<span />通过 CSS 模仿复选框的虚假复选框。span的事件click触发change了真实checkbox的事件:

// 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 将侦听该事件处理程序。换句话说,当用户点击虚假复选框时,绑定的 Knockout 模型会更新。但是,不起作用的是更新模型。如果我打电话:

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

模型得到更新,但假 UI 没有更新。我已将此问题追溯到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)并触发更改事件。但是,这是行不通的。到目前为止,这是我所知道的:

  1. 如果我从等式中删除 Knockout.js,则$(element).prop('checked', value).trigger('change');效果很好。所以,knockout.js 以某种方式搞砸了这个事件。是否取消绑定该事件处理程序?
  2. 我已经确认与假复选框绑定代码中$(element)的内容相同this.$input。我可以在这个元素上设置其他 expando 属性,它们就会显示出来。
  3. 我尝试了一些方法来尝试调试 Knockout.js 和 jQuery 以查看事件是否仍然绑定,但是我并没有真正使用这种方法。我的预感是 Knockout.js 不知何故用change自己的内部事件处理程序替换了事件处理程序,并且现有的绑定被删除了。我还没有找到方法来确认这一点。

我的问题:主要是,我正在寻找解决此问题的方法。Knockout.js 是否会删除change应用模型之前存在的现有事件?调试此代码并弄清楚到底发生了什么的下一步是什么?

4

6 回答 6

2

无需覆盖基本检查处理程序,只需创建您自己的自定义绑定来更新 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/

[编辑] - 我想我匆匆阅读了你的问题,并没有明白你所问的症结所在。无论如何,我都会在这里留下我的答案。

于 2013-08-27T08:47:09.323 回答
0

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

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

难道不能尝试一下吗?也许它绑定到 click 事件而不是 change 事件?在你的 else 语句中试试这个:

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

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

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

只是一个想法,可能是要尝试的东西。希望能帮助到你!

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

/J。

于 2013-08-27T08:19:02.850 回答
0

尝试将复选框对象创建为 jQuery 插件,并创建剔除自定义绑定以将其绑定到您的视图模型。我已经建立了一个 jsfiddle 就是这样做的:http: //jsfiddle.net/markhagers/zee4z/3/

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

    ko.applyBindings(viewModel);
});

(我必须添加此代码段,否则我的答案未被接受,请参阅 jsfiddle 以获取完整代码示例)。对不起,看起来代码很多,但是插件部分本来是一个独立的jquery插件,后来添加了knockout bindings,其中一些代码已经被knockout bindings淘汰了。可以将其放入自己的文件中以减少混乱。创建自定义绑定记录在淘汰网站上。

于 2013-08-16T11:16:51.510 回答
0

尝试触发和侦听自定义事件:

element.checked = value;
element.dispatchEvent(new Event("forcedChange"));
于 2013-08-15T12:37:10.843 回答
0

只是头脑风暴,但是这个第三方控件是使用事件命名空间开发的吗?可能就像您建议的那样,淘汰赛和此控件以某种方式争夺默认的“更改”事件。当我开发小部件和应用程序时,我总是在使用 jQuery 绑定事件时使用事件命名空间。

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

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

不过,我可能完全不在基地。

于 2013-08-21T14:46:10.437 回答
0

听起来您遇到的问题不是由于淘汰赛事件和另一组代码(例如,更改复选框的 CSS)的冲突,而是由于 Javascript 线程和事件中的冲突线程,被处理。

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

这个(http://ejohn.org/blog/how-javascript-timers-work/)示例使用计时器演示了这个想法,但我认为您目前的情况可能会遇到同样的问题。

要解决它,您可以编写一个简短的函数,例如:

函数 doBoth(checkboxid){

$("#checkboxid").addclass("prettychecked");

$("#checkboxid").prop('checked', true);

}

重要的一点是每个动作都是按顺序发生的,没有鼠标事件中断的可能性。

希望这可以帮助!

于 2013-09-09T20:52:28.527 回答