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

时间:2022-12-03 07:53:39

Ok I've been trying to unravel this mess for a few hours now and have gotten nowhere, analogous to a dog chasing its tail. Here's the situation.

好吧,我一直试图解开这个烂摊子几个小时而且已经无处可去,类似于追逐尾巴的狗。这是情况。

I'm using Knockout.js for my UI, which works great by itself. However, I'm trying to use some third party code that makes dropdowns and checkboxes look all pretty. Actually I'm not even sure if this is a third party library or just something our designers wrote. This code hides the real checkbox and replaces it with a fake <span /> that mimics a checkbox through CSS. The click event of the span triggers the change event of the real checkbox:

我正在使用Knockout.js作为我的UI,它本身很好用。但是,我正在尝试使用一些第三方代码,使下拉列表和复选框看起来很漂亮。实际上我甚至不确定这是第三方库还是我们设计师写的东西。此代码隐藏了真实的复选框,并将其替换为假的,它通过CSS模仿复选框。 span的click事件触发真实复选框的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);

This actually works with Knockout.js, since Knockout will listen to that event handler. In other words, when the user clicks the fake checkbox, the bound Knockout model gets updated. However, what does not work is updating the model. If I call:

这实际上适用于Knockout.js,因为Knockout将监听该事件处理程序。换句话说,当用户单击假复选框时,绑定的Knockout模型会更新。但是,不起作用的是更新模型。如果我打电话:

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

The model gets updated, but the fake UI is not updated. I've traced this problem down to the code in ko.bindingHandlers.checked.update which does the following:

模型会更新,但虚假的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;

Basically, the element.checked property is set, but no events are fired. Thus, the _changeEvent function is never called. So, I've implemented my own ko.bindingHandlers.checked.update function, which is a copy of the built-in one. In theory, this is all I should need to do:

基本上,已设置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);
      }
   };

Rather than setting element.checked, I instead call .prop('checked', value) and trigger the change event. However, this is not working. Here's what I know so far:

而不是设置element.checked,而是调用.prop('checked',value)并触发change事件。但是,这不起作用。这是我目前所知道的:

  1. If I remove Knockout.js from the equation, $(element).prop('checked', value).trigger('change'); works perfectly fine. So, knockout.js is screwing with the event some how. Is it unbinding that event handler?
  2. 如果我从等式中删除Knockout.js,$(element).prop('checked',value).trigger('change');工作得非常好。所以,knockout.js正在搞这个事件。它解除了那个事件处理程序吗?
  3. I've confirmed $(element) is the same thing as this.$input in the fake checkbox binding code. I can set other expando properties on this element, and they show up.
  4. 我已经确认$(元素)与此相同。在假复选框绑定代码中输入$。我可以在这个元素上设置其他expando属性,然后它们就会显示出来。
  5. I've tried a few approaches to try to debug into Knockout.js and jQuery to see if the event is still bound, however I haven't really gotten anywhere with this approach. My hunch is that Knockout.js somehow replaced the change event handler with its own internal one, and existing bindings were removed. I haven't found a way to confirm this yet.
  6. 我尝试了一些方法来尝试调试Knockout.js和jQuery以查看事件是否仍然受限,但是我还没有真正得到这种方法。我的预感是,Knockout.js以某种方式用自己的内部处理程序替换了更改事件处理程序,并删除了现有的绑定。我还没有办法确认这一点。

My Question: Mainly, I'm looking for a solution to this problem. Does Knockout.js remove existing change events that were there before the model was applied? What are the next steps in debugging this code and figuring out exactly what's going on?

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

6 个解决方案

#1


2  

Rather than overriding the base checked handler, just create your own custom binding to update the UI.

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

Here's an example of a model and a custom binding to handle updating the 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');
    }
};

I'll bind the model to the following HTML:

我将模型绑定到以下HTML:

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

And really that's all I'll need to do.

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

Here's an example: http://jsfiddle.net/badsyntax/4Cy3y/

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

[edit] - i think i rushed through reading your question and didn't get the crux of what you were asking. I'll leave my answer here in any case.

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

#2


0  

Try triggering and listening also for a custom event:

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

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

#3


0  

Try creating the checkbox object as a jQuery plugin and create knockout custom bindings to bind it to your viewmodel. I have set up a jsfiddle that does just that: http://jsfiddle.net/markhagers/zee4z/3/

尝试将checkbox对象创建为jQuery插件,并创建knockout自定义绑定以将其绑定到viewmodel。我已经建立了一个jsfiddle就是这样做的:http://jsfiddle.net/markhagers/zee4z/3/

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

    ko.applyBindings(viewModel);
});

(I had to add this snippet, or my answer wasn't accepted, see the jsfiddle for the full code example). Sorry, it looks like a lot of code, but the plugin part was originally a standalone jquery plugin, with the knockout bindings added later, some code in it has been obsoleted by the knockout bindings. It can be put into it's own file to reduce clutter. Creating custom bindings is documented on the knockout website.

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

#4


0  

Just brainstorming, but was this third-party control developed using event namespaces? It could be like you suggest, that knockout and this control are fighting over the default 'change' event in some way. When I develop widgets and applications I always use event namespaces when binding events using jQuery.

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

Example using a 'myWidget' namespace:

使用'myWidget'命名空间的示例:

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

I could be completely off base though.

我可以完全离开基地。

#5


0  

Just a quick thought. Your designers seems to trigger the click and change event in this line:

只是一个快速的想法。您的设计师似乎在此行中触发了click和change事件:

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

Couldn't that be something to try? Maybe it's bound to the click event rather than the change event? Try this in you else-statement:

难道不能尝试一下吗?也许它与click事件绑定而不是更改事件?在你的声明中试试这个:

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

What i have understood that .click() does: It mimics a mouseclick and at the same time triggers the click event (Duh?).

我明白.click()的作用是:它模仿鼠标点击并同时触发点击事件(Duh?)。

I know that sometimes (like when in a radio or checkbox) some coders are binding the event to the click rather than change since this is more likely to fire at the correct time (in IE7 the change event fires in a really weird way).

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

Just a thought, might be something to try out. Hope it helps!

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

EDIT: I saw an answer about namespaces in your events and custom events. This could also help you, hope it's not a too large change.

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

/J.

/ J。

#6


0  

It sounds like the issue you're having is not due to a conflict in events from knockout and another set of code (for example, changing the CSS of the checkboxes) but due to a conflict in how the Javascript thread, and the events in the thread, are handled.

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

Javascript is handled in the browser as a single thread: all processes run sequentially and any event that interrupts the sequence might cause your event to fail. If you can call each of the functions independently but there is interference when you include both in your script, the onclick or onmousedown event that triggers the CSS change for the checkboxes might interfere with the knockout script. In this "single thread" all events can influence the execution of another, even mouse movement.

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

This (http://ejohn.org/blog/how-javascript-timers-work/) example demonstrates this idea using timers but I think your current situation could be suffering from the same problem.

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

To solve it, you might write a short function such as:

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

function doBoth(checkboxid){

function doBoth(checkboxid){

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

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

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

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

}

}

The important point is that each action takes place in sequence without the possibility of a mouse event interrupting.

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

Hope this helps!

希望这可以帮助!

#1


2  

Rather than overriding the base checked handler, just create your own custom binding to update the UI.

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

Here's an example of a model and a custom binding to handle updating the 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');
    }
};

I'll bind the model to the following HTML:

我将模型绑定到以下HTML:

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

And really that's all I'll need to do.

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

Here's an example: http://jsfiddle.net/badsyntax/4Cy3y/

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

[edit] - i think i rushed through reading your question and didn't get the crux of what you were asking. I'll leave my answer here in any case.

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

#2


0  

Try triggering and listening also for a custom event:

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

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

#3


0  

Try creating the checkbox object as a jQuery plugin and create knockout custom bindings to bind it to your viewmodel. I have set up a jsfiddle that does just that: http://jsfiddle.net/markhagers/zee4z/3/

尝试将checkbox对象创建为jQuery插件,并创建knockout自定义绑定以将其绑定到viewmodel。我已经建立了一个jsfiddle就是这样做的:http://jsfiddle.net/markhagers/zee4z/3/

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

    ko.applyBindings(viewModel);
});

(I had to add this snippet, or my answer wasn't accepted, see the jsfiddle for the full code example). Sorry, it looks like a lot of code, but the plugin part was originally a standalone jquery plugin, with the knockout bindings added later, some code in it has been obsoleted by the knockout bindings. It can be put into it's own file to reduce clutter. Creating custom bindings is documented on the knockout website.

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

#4


0  

Just brainstorming, but was this third-party control developed using event namespaces? It could be like you suggest, that knockout and this control are fighting over the default 'change' event in some way. When I develop widgets and applications I always use event namespaces when binding events using jQuery.

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

Example using a 'myWidget' namespace:

使用'myWidget'命名空间的示例:

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

I could be completely off base though.

我可以完全离开基地。

#5


0  

Just a quick thought. Your designers seems to trigger the click and change event in this line:

只是一个快速的想法。您的设计师似乎在此行中触发了click和change事件:

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

Couldn't that be something to try? Maybe it's bound to the click event rather than the change event? Try this in you else-statement:

难道不能尝试一下吗?也许它与click事件绑定而不是更改事件?在你的声明中试试这个:

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

What i have understood that .click() does: It mimics a mouseclick and at the same time triggers the click event (Duh?).

我明白.click()的作用是:它模仿鼠标点击并同时触发点击事件(Duh?)。

I know that sometimes (like when in a radio or checkbox) some coders are binding the event to the click rather than change since this is more likely to fire at the correct time (in IE7 the change event fires in a really weird way).

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

Just a thought, might be something to try out. Hope it helps!

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

EDIT: I saw an answer about namespaces in your events and custom events. This could also help you, hope it's not a too large change.

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

/J.

/ J。

#6


0  

It sounds like the issue you're having is not due to a conflict in events from knockout and another set of code (for example, changing the CSS of the checkboxes) but due to a conflict in how the Javascript thread, and the events in the thread, are handled.

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

Javascript is handled in the browser as a single thread: all processes run sequentially and any event that interrupts the sequence might cause your event to fail. If you can call each of the functions independently but there is interference when you include both in your script, the onclick or onmousedown event that triggers the CSS change for the checkboxes might interfere with the knockout script. In this "single thread" all events can influence the execution of another, even mouse movement.

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

This (http://ejohn.org/blog/how-javascript-timers-work/) example demonstrates this idea using timers but I think your current situation could be suffering from the same problem.

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

To solve it, you might write a short function such as:

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

function doBoth(checkboxid){

function doBoth(checkboxid){

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

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

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

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

}

}

The important point is that each action takes place in sequence without the possibility of a mouse event interrupting.

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

Hope this helps!

希望这可以帮助!