Knockout.js似乎在抨击我的jQuery事件处理程序,真是太粗鲁了
Knockout.js seems to be clobbering my jQuery event handlers, how rude
好吧,我已经试着解开这个烂摊子好几个小时了,但一无所获,就像狗在追尾巴一样。情况是这样的。
我的UI使用Knockout.js,它本身就很好用。然而,我正在尝试使用一些第三方代码,使下拉列表和复选框看起来都很漂亮。事实上,我甚至不确定这是第三方图书馆,还是我们的设计师写的东西。此代码隐藏真实复选框,并将其替换为通过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);
这实际上适用于Knockout.js,因为Knockout将监听该事件处理程序。换句话说,当用户单击fake复选框时,绑定的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)
并触发更改事件。然而,这并不奏效。以下是我目前所知道的:
- 如果我从等式中删除Knockout.js,
$(element).prop('checked', value).trigger('change');
就可以很好地工作。所以,knockout.js在某种程度上破坏了这个事件。它是否解除了对事件处理程序的绑定 - 我已经确认
$(element)
与伪复选框绑定代码中的this.$input
相同。我可以在这个元素上设置其他expando属性,它们就会显示出来 - 我尝试了几种方法来调试Knockout.js和jQuery,看看事件是否仍然绑定,但这种方法并没有真正取得进展。我的直觉是Knockout.js以某种方式用自己的内部事件处理程序替换了
change
事件处理程序,并且删除了现有的绑定。我还没有找到证实这一点的方法
我的问题:主要是,我正在寻找这个问题的解决方案。Knockout.js是否删除应用模型之前存在的现有change
事件?调试此代码并弄清楚到底发生了什么的下一步是什么?
与其覆盖基本检查的处理程序,不如创建自己的自定义绑定来更新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"));
尝试将复选框对象创建为jQuery插件,并创建淘汰的自定义绑定将其绑定到视图模型。我已经建立了一个jsfiddle来做到这一点:http://jsfiddle.net/markhagers/zee4z/3/
$(function () {
var viewModel = {
truthyValue1: ko.observable(false),
truthyValue2: ko.observable(true)
};
ko.applyBindings(viewModel);
});
(我不得不添加这个片段,否则我的回答不被接受,请参阅jsfiddle以获取完整的代码示例)。抱歉,它看起来有很多代码,但插件部分最初是一个独立的jquery插件,后来添加了淘汰绑定,其中的一些代码已经被淘汰绑定所淘汰。它可以放在自己的文件中,以减少混乱。创建自定义绑定的文档记录在淘汰网站上。
只是集思广益,但这个第三方控件是使用事件名称空间开发的吗?正如你所建议的,淘汰赛和这种控制在某种程度上正在为默认的"改变"事件而斗争。当我开发小部件和应用程序时,在使用jQuery绑定事件时,我总是使用事件名称空间。
使用"myWidget"命名空间的示例:
$('#element').on('change.myWidget', function(e) {
// handle the event
}
不过我可能完全偏离了底线。
只是一个快速思考。您的设计人员似乎触发了以下行中的点击和更改事件:
self.$input.click().trigger('change');
难道不能尝试一下吗?也许它绑定到点击事件而不是更改事件?试试你的其他声明:
$(element).prop('checked', value).click().trigger('change'); // <--- this should work!
我所理解的是.click()的作用:它模仿鼠标点击,同时触发点击事件(Duh?)。
我知道有时(比如在收音机或复选框中),一些编码程序会将事件绑定到点击而不是更改,因为这更有可能在正确的时间触发(在IE7中,更改事件以一种非常奇怪的方式触发)。
只是一个想法,可能是一些尝试。希望它能有所帮助!
编辑:我在您的事件和自定义事件中看到了一个关于名称空间的答案。这也可以帮助你,希望这不是一个太大的变化。
/J。
听起来,您遇到的问题并不是由于淘汰和另一组代码中的事件冲突(例如,更改复选框的CSS),而是由于如何处理Javascript线程和线程中的事件的冲突。
Javascript在浏览器中作为一个线程处理:所有进程都按顺序运行,任何中断序列的事件都可能导致事件失败。如果您可以独立调用每个函数,但在脚本中同时包含这两个函数时会产生干扰,那么触发复选框CSS更改的onclick或onmousedown事件可能会干扰淘汰脚本。在这个"单线程"中,所有事件都可以影响另一个线程的执行,甚至鼠标的移动。
这个(http://ejohn.org/blog/how-javascript-timers-work/)这个例子演示了使用计时器的想法,但我认为您当前的情况可能也遇到了同样的问题。
为了解决这个问题,您可以编写一个简短的函数,例如:
函数doBoth(checkboxid){
$("#checkboxid").addclass("prettychecked");
$("#checkboxid").prop("checked",true);
}
重要的一点是,每个动作都是按顺序进行的,没有鼠标事件中断的可能性。
希望这能有所帮助!
- keyup事件处理程序更改焦点不适用于快速键入
- 提示使用服务器端事件处理程序激活JavaScript
- 将事件处理程序绑定到任何可能的事件
- 正在将事件处理程序添加到不存在的类
- 在循环中附加事件处理程序时出现浏览器性能问题
- 在同一个javascript事件处理程序中调用不同的函数
- 有没有一种方法可以让内联事件处理程序在元素创建后立即执行
- 检查事件处理程序参数
- 实现延迟的jquery更改事件处理程序
- 如何使用Node.js在JavaScript模块文件之间使用事件处理程序
- 如何使jQuery的“bind”或“on”事件处理程序幂等
- 带有参数的Javascript事件处理程序
- Jquery事件处理程序仅适用于匿名函数
- 如何从另一个处理程序内部取消JavaScript事件处理程序函数的执行
- 如何在jQuery事件处理程序中存储和重用超时
- 如何向onClick事件处理程序传递一个接受参数的函数,并且仍然将该函数绑定到组件's”;这个“;上下文
- 异步处理所有事件处理程序的方法
- jsplumb中的Click事件处理程序丢失“;这个“;对象
- 构造函数中的事件处理程序与构造函数外的事件处理函数的行为不同
- 如何在事件处理程序的回调中防止Default