IE 8中的Knockout和jQuery冲突?

Jas*_*ank 5 jquery internet-explorer-8 knockout.js

我们在需要支持IE 8的应用程序中使用Knockout和jQuery(在其他几个浏览器中).我们有一个案例需要对一个元素使用Knockout点击绑定,对另一个元素使用jQuery点击处理程序.但是,我们发现这两种类型的点击处理程序在IE 8中相互冲突(它们在我们所有其他支持的浏览器中可以很好地协同工作).

我们将问题简化为一个简单的可重复的例子.如果您的计算机上没有IE 8,可以通过选择"浏览器模式:IE8"在较新版本的IE中测试它:

在此输入图像描述

使用该设置,您可以在此jsbin中查看冲突.这段代码是:

<html>
<head>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
    <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.0.0.js"></script>

    <script type="text/javascript">
        $(document).ready(function () {
            ko.applyBindings(function () {
                var viewModel = new Object();

                viewModel.CanViewPage = true;

                viewModel.alertA = function () {
                    alert("A");
                };

                $('#B').click(function () {
                    alert("B");
                });

                return viewModel;
            }());
        });
    </script>
</head>
<body>
    <!-- ko if: CanViewPage -->
        <span data-bind="click: alertA">A</span>
        <span id="B">B</span>
    <!-- /ko -->
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

单击"A"时,将显示预期警报.但是,当您单击"B"时,alert("A")会错误地调用:

在此输入图像描述

我们找到了两个解决这个问题的"解决方案",但它们都不适用于我们的应用程序.让这两个点击处理程序同时在IE 8中工作的第一种方法是删除ko if: CanViewPage.这可以在这个jsbin中看到.这个"解决方案"是不可行的,因为它是我们在许多页面上的这个大型应用程序中使用的核心页面视图权限机制.

我们发现的第二个"解决方案"是颠倒导入jQuery和Knockout的脚本标签的顺序.这可以在这个jsbin中看到.这种"解决方案"也不可行,因为这些导入是通过母版页包装器控制的 - 改变这需要大量的QA工作来重新测试整个应用程序的错误.

请注意,因为我们必须支持IE8,所以jQuery 2.x或更新版本不是一个选项.

有没有人有这些限制的另一种解决方案?任何人都可以解释为什么会这样吗?

Mic*_*est 5

指导

正如您所指出的,设置jQuery事件处理程序和if绑定的组合不能正常工作.如果与DOM的所有交互都通过Knockout进行,则Knockout通常效果最佳.例如,如果我将您的示例扩展CanViewPage为最初falsetrue后来变为更晚,则"B"的事件处理程序将消失.http://jsbin.com/uyUTuSa/16/edit

既然您已声明不能将Knockout用于此事件处理程序,那么Bradley的解决方案就是一个很好的解决方法.如果您知道CanViewPage永远不会改变(不可观察),您可以使用的另一个解决方案是自定义绑定,它只是在假值上清除其内容而不进行任何复制或清理.

ko.bindingHandlers.ifLight = {
    init: function(element, valueAccessor) {
        if (!valueAccessor()) {
            ko.virtualElements.emptyNode(element);
        }
    }
};
ko.virtualElements.allowedBindings.ifLight = true;
Run Code Online (Sandbox Code Playgroud)

http://jsbin.com/uyUTuSa/18/edit

技术细节

jQuery通过在元素上设置内部属性来跟踪要为元素调用的事件处理程序.为此属性分配了一个唯一编号,用于在某种内部数据存储中查找处理程序.

Knockout if绑定首先保存内容元素的副本,cloneNode然后调用ko.cleanNode副本,该副本也调用jQuery.cleanData.这将从副本中删除所有数据和事件处理程序.

通常这一切都正常,但在Internet Explorer 8(及以下)中,"expando"属性将转换为元素属性,并在使用时复制cloneNode.当jQuery清除复制的节点时,事件处理程序数据也会从原始节点中删除.

当Knockout为"A"元素添加click处理程序时,它使用jQuery来注册处理程序.jQuery似乎重新使用已清理元素的数据(和数字),因此"A"元素与"B"元素的编号相同.


Bra*_*ger 3

为您提供的解决方案是改变

$('#B').click(function () {
    alert("B");
});
Run Code Online (Sandbox Code Playgroud)

$(document).on('click', '#B',function () {
    alert("B");
});
Run Code Online (Sandbox Code Playgroud)

请参阅这个jsbin

我不知道为什么会发生这种情况,但我怀疑淘汰“if”会导致您的元素在应用 ko.applyBindings() 时丢失其单击事件。

如果是这种情况,那么它起作用的原因是因为您现在正在将事件应用到未被销毁的父元素上。

有关 jQuery 事件处理程序的更多信息,请参阅这篇优秀文章:http://www.elijahmanor.com/differences- Between-jquery-bind-vs-live-vs-delegate-vs-on/