【问题标题】:Knockout.js seems to be clobbering my jQuery event handlers, how rudeKnockout.js 似乎正在破坏我的 jQuery 事件处理程序,多么粗鲁
【发布时间】:2013-08-16 20:54:35
【问题描述】:

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

我在我的 UI 中使用了 Knockout.js,它本身就很好用。但是,我正在尝试使用一些第三方代码,使下拉菜单和复选框看起来都很漂亮。实际上,我什至不确定这是第三方库还是我们的设计师写的东西。此代码隐藏真正的复选框,并将其替换为通过 CSS 模仿复选框的假 <span />。 span的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 将侦听该事件处理程序。换句话说,当用户点击虚假复选框时,绑定的 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 事件?调试此代码并弄清楚到底发生了什么的下一步是什么?

【问题讨论】:

  • +1 多么粗鲁,确实:)
  • +1 写得很好的问题。 (还没有)对此进行深入研究,但是浏览 KO 源代码,我确实看到utils.js 中的RegisterEventHandler 在涉及 jQuery 时对复选框的特殊处理有相当长的评论。也许这是一个线索?
  • @Esailija - 是的,我想我必须构建一个完全独立的可重现示例。叹息。
  • 在 jQuery 中使用 Knockout 时,包含顺序很重要。只有在包含 Knockout 时它存在时,Knockout 才使用 jQuery。
  • @Jeroen - 你可能是对的,正如评论的最后一行所说:“// 通过接收处理程序并在它运行之前应用正确的检查来解决这个问题。”

标签: javascript jquery knockout.js


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

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

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

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

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

        【讨论】:

          【解决方案5】:

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

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

          这不是可以尝试的吗?也许它绑定到 click 事件而不是 change 事件?在你的 else 语句中试试这个:

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

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

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

          只是一个想法,可能需要尝试一下。 希望对您有所帮助!

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

          /J.

          【讨论】:

            【解决方案6】:

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

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

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

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

            函数 doBoth(checkboxid){

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

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

            }

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

            希望这会有所帮助!

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-03-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-12-16
              相关资源
              最近更新 更多