Knockout树视图搜索保留层级结构 嵌套节点过滤失效如何解决
Knockout.js 树视图嵌套节点过滤修复方案
问题根因
- 原有
filteredObjectOrNull逻辑仅在子节点匹配时才返回父节点,忽略了父节点本身匹配搜索词的场景,会导致父节点命中时丢失全部子节点 - 冗余函数
filterArrayWithQuery存在未定义变量obj的语法错误,可直接删除避免干扰
核心逻辑调整
过滤递归逻辑需覆盖两种合法保留场景:
- 当前节点自身名称匹配搜索词,直接返回当前节点+所有子节点,保留完整树结构
- 当前节点自身不匹配,但存在任意子节点匹配,返回当前节点+过滤后的子节点列表
- 以上都不满足返回null,后续统一过滤
修复后完整代码
JavaScript 部分
var ViewModel = function() { var self = this; self.itemFilter = ko.observable(""); self.items = ko.observableArray([{ name: "name3", viewable: true, children: [{ name: "name3-1", viewable: true, children: [ {name: "name3-thordone", viewable: true}, {name: "name3-thirdtwo", viewable: true}, ] }] } ]); self.matchText = function(source, query) { return source.toLowerCase().indexOf(query.toLowerCase()) !== -1; }; self.nonNullItems = function(arr) { return arr.filter(function(x) { return x !== null; }); }; self.filteredObjectOrNull = function(obj, query) { // 当前节点本身匹配,直接返回完整节点保留所有子节点 if (self.matchText(obj.name, query)) { return obj; } // 当前节点不匹配,递归过滤子节点 if (obj.hasOwnProperty('children')) { var filteredChildren = self.nonNullItems(obj.children.map(function(child) { return self.filteredObjectOrNull(child, query); })); // 存在匹配的子节点,返回当前节点+过滤后的子节点 if (filteredChildren.length > 0) { return { name: obj.name, viewable: obj.viewable, children: filteredChildren }; } } // 无匹配返回null return null; }; self.filterItems = ko.computed(function() { var filter = self.itemFilter().trim(); if (filter === "") { return self.items(); } return self.nonNullItems(self.items().map(function(x) { return self.filteredObjectOrNull(x, filter); })); }); }; ko.applyBindings(new ViewModel());
HTML 部分(无需修改)
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script> <input data-bind="value: itemFilter, valueUpdate: 'afterkeydown'" type="text" /> <p>Filter term: <span data-bind="text: itemFilter"></span></p> <ul data-bind="template: { name: 'nav-list-template', foreach: filterItems }"></ul> <script type="text/html" id="nav-list-template"> <li> <a data-bind="text: name"></a> <!-- ko if: typeof children !== 'undefined' && children.length > 0 --> <ul> <!-- ko template: { name: 'nav-list-template', foreach: children } --> <!--/ko --> </ul> <!-- /ko --> </li> </script>
额外优化说明
- 将
filterItems改为Knockout计算属性(ko.computed),自动依赖响应式变量,避免重复执行,性能更优 matchText方法统一将搜索词转小写,解决大小写敏感导致的匹配遗漏问题- 递归子节点时用
map代替filter,逻辑更清晰,避免隐式类型转换导致的异常
内容的提问来源于stack exchange,提问作者learner
相关产品推荐
相关产品推荐

