首页 > 解决方案 > 为什么在手动重新排序和恢复 dom 项目后,淘汰模板绑定停止工作?

问题描述

我正在使用淘汰赛 foreach (更具体地说,template: { foreach: items })绑定来显示元素列表。然后我继续采取以下行动:

  1. 交换可观察数组的第一个和第二个元素。正如预期的那样,我看到屏幕上反映的变化。
  2. 重复上一个操作以恢复到初始状态。同样,这按预期工作。
  3. 现在,交换第一个和第二个 DOM 元素。正如预期的那样,我看到屏幕上反映的变化。
  4. 重复上一个操作以恢复到初始状态。同样,这按预期工作。

尽管我们已经手动篡改了 DOM,但我们已经完全恢复到初始状态,而在 DOM 篡改期间没有调用敲除。这意味着状态恢复到最后一次淘汰赛意识到它,所以它应该看起来像淘汰赛一样,好像什么都没有改变一样开始。但是,如果我再次执行第一个操作,即交换数组中的前两个元素,则更改不会反映在屏幕上。

这是一个 jsfiddle 来说明问题:https ://jsfiddle.net/k7u5wep9/ 。

我知道手动篡改由淘汰赛管理的 DOM 是一个坏主意,并且可能导致未定义的行为。不幸的是,由于第三方代码,这在我的情况下是不可避免的。让我难过的是,即使在将手动编辑恢复到确切的初始状态之后,淘汰赛仍然无法按预期工作。

我的问题是:是什么导致了这种行为?然后,如何解决它?

标签: javascriptknockout.jsknockout-3.0

解决方案


事实证明,这里并没有什么神奇的事情发生。我犯的错误是只考虑元素而不是所有节点。淘汰模板绑定在重新排序时保留所有节点的记录,而不仅仅是元素。

在手动编辑 DOM 之前,模板绑定的子节点为:

NodeList(6) [text, div, text, text, div, text].

使用手动交换前两个元素parent.insertBefore(parent.children[1], parent.children[0])后,变为:

NodeList(6) [text, div, div, text, text, text].

重复该动作产生:

NodeList(6) [text, div, div, text, text, text].

虽然这与仅引用元素时的初始状态相同,但引用所有节点时则完全不同。

解决方案现在变得清晰。执行正确手动交换的一种方法是替换

parent.insertBefore(parent.children[1], parent.children[0]);

let nexts = [parent.children[0].nextSibling, parent.children[1].nextSibling];
parent.insertBefore(parent.children[1], nexts[0]);
parent.insertBefore(parent.children[0], nexts[1]);

https://jsfiddle.net/k7u5wep9/2/中所示。

显然,当之前/之后没有文本节点时必须更加小心,但想法保持不变。


推荐阅读