IE 8中的Knockout和jQuery冲突?

时间:2013-12-11 20:03:45

标签: jquery knockout.js internet-explorer-8

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

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

enter image description here

使用该设置,您可以在this 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>

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

enter image description here

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

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

请注意,由于我们必须支持IE8,因此jQuery 2.x或更新版本为not an option

在给出这些限制的情况下,是否有人有其他解任何人都可以解释为什么会这样吗?

2 个答案:

答案 0 :(得分:5)

指导

正如您所指出的,设置jQuery事件处理程序和if绑定的组合不能正常工作。如果与DOM的所有交互都通过Knockout进行,则Knockout通常效果最佳。例如,如果我展开您的示例以使CanViewPage最初为false但稍后变为true,则“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;

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”元素的编号相同。

答案 1 :(得分:3)

您的解决方案是更改

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

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

请参阅此jsbin

我不知道为什么会发生这种情况,但我怀疑敲除“if”会导致你的元素在应用ko.applyBindings()时失去其click事件。

如果是这种情况,其原因在于您现在将事件应用于未被销毁的父元素。

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