You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Knockout树视图搜索保留层级结构 嵌套节点过滤失效如何解决

Knockout.js 树视图嵌套节点过滤修复方案

问题根因

  • 原有filteredObjectOrNull逻辑仅在子节点匹配时才返回父节点,忽略了父节点本身匹配搜索词的场景,会导致父节点命中时丢失全部子节点
  • 冗余函数filterArrayWithQuery存在未定义变量obj的语法错误,可直接删除避免干扰

核心逻辑调整

过滤递归逻辑需覆盖两种合法保留场景:

  1. 当前节点自身名称匹配搜索词,直接返回当前节点+所有子节点,保留完整树结构
  2. 当前节点自身不匹配,但存在任意子节点匹配,返回当前节点+过滤后的子节点列表
  3. 以上都不满足返回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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 04:24:03