我有一个带有两个视图的简单应用程序,一个“应用程序”模型和两个视图模型。我有兴趣研究如何取消绑定 jQuery 事件。
应用程序视图模型具有以下属性:
var self = this;
self.viewModel = ko.observable(null);
最重要的 HTML 片段位于主模板中,即:
<!-- ko if: viewModel -->
<div data-bind="template:{name:viewModel().template, data:viewModel(), afterRender: viewModel().viewDidRender}"></div>
<!-- /ko -->
在应用模型中,初始化后,视图模型被加载。每个视图模型都有一个简单的模板属性,它是一个引用要呈现的 html 模板的字符串。模板声明中的数据项将 knockoutjs 上下文设置为当前视图模型。afterRender 绑定确保调用 ViewModel 的 viewDidRender 方法(使用容器 html 元素作为参数)。
这意味着我可以执行以下操作:
self.viewDidRender = function (parentElement) {
self.containerElement = parentElement;
$("html").on("click", function (event) {
var elements = $(event.target).parents();
for (var i = 0; i < elements.length; i++)
if (elements[i] == self.containerElement[0] || elements[i] == self.containerElement) {
alert("the target exists within the parent element");
return;
}
self.open(false);
});
};
有一个要求是我们拦截html元素的点击事件。如果事件发生在目标位于子视图的父元素内的位置,则忽略该事件,否则将 'open' 设置为 false(这实际上是您在 iOS 中使用 PopOvers 看到的轻度关闭机制的一部分)
我发现当我在两个主视图之间切换时(每次渲染时都会调用此示例视图的 viewDidRender),上面的正文单击处理程序被绑定了多次。
我认为这是 JavaScript 内存泄漏。应该去哪里清理这些绑定?我可以调用 $("html").off(...) 等...但是在哪里?模板绑定的 beforeRemove 事件不会为纯模板项调用......仅适用于 foreach 绑定。
我可以在所有子视图模型中创建另一个方法,例如“viewWillHide”,因此每次将新视图推送到应用模型的 ViewModel 属性时,我都可以尝试在视图模型上调用 viewWillHide 方法。这会奏效。但是,该应用程序比我在这里演示的要复杂。实际上存在视图模型和嵌套模板的层次结构。
手动操作是一种选择;模板绑定的 afterRender 没有对立面,这似乎很奇怪。
那你会怎么做?手动方法?即使它会导致整个视图模型层次结构发生级联变化,以便将 viewWillHide 方法从顶部(应用程序模型)向下传播到较小的视图模型。
谢谢