前言

最近公司做一个项目用到zTree,zTree功能强大就不用多说了,相信用过的人都知道。
公司项目因为要展示的节点非常多,所以要求要实现搜索节点的功能,zTree确实很强大,它提供了getNodesByParamByFuzzy方法可根据关键字进行模糊查询得到想要的节点,极其方便。但是问题来了,因为要读取节点的数据量很大,节点的数量有几万个甚至更多,考虑到性能和时间上的问题,因此不能一次性把全部节点数据读取出来,这里我采用的是zTree自带的分批异步加载模式。但是这样就又造成了另外一个问题,再使用getNodesByParamByFuzzy这个方法时便只能找到已经加载出来的节点,而无法找到还没有进行加载的节点,从而使用户体验不好。

分批异步加载

加载所有父节点

为了解决这个问题,刚开始的想法是每次都去数据库查询数据,每次只显示搜索到的第一条结果,然后再进行下一条查询,查到最后一条时又返回第一条,事实这样的思路是行的通的,但是这样实现起来非常麻烦,而在项目中通常都不止一棵树,难不成每棵树都要这样,为了这小小的功能却要写如此多的代码实在恶心,废话不多说,下面是我自己想的认为比较好的方法。看下面代码:

// 节点加载完的回调函数,加载方式依旧是分批加载,但是不是等用户展开节点才去加
// 载,而是让它自动完成加载,这里不好的地方是依旧要加载全部数据,所以必须等待
// 它加载完才能使用搜索功能
function onAsyncSuccess(event, treeId, treeNode, msg) {
    var zTreeObj = $.fn.zTree.getZTreeObj();
    // 这个方法是将标准 JSON 嵌套格式的数据转换为简单 Array 格式
    var nodes = zTreeObj.transformToArray(zTreeObj.getNodes()); 
    for (var i = 0; i < nodes.length; i++) {
        // 判断节点是否已经加载过,如果已经加载过则不需要再加载
        if (!nodes[i].zAsync) {
            zTreeObj.reAsyncChildNodes(nodes[i], '', true);
        }
    }
}

等待全部树节点加载完

所以还必须定义多一个判断树节点是否已经全部加载完的方法。这个方法我是参考《精通JavaScript》上面关于等待页面加载完的方法来写的。

function treeAsyncReady(treeId, f) {
    // 如果树已经加载完,马上执行函数
    if (treeAsyncReady.done) {
        return f();
    }
    
    var zTreeObj = $.fn.zTree.getZTreeObj();
    
    treeAsyncReady.timer = setInterval(function() {
        if (treeAsyncReady.done) {
            return false;
        }
        
        // 获取没有异步加载过的节点
        var nodes = zTreeObj.getNodesByFilter(funciton(node) {
            return !node.zAsync;
        });
        // 如果节点数为零则说明已经加载完
        if (nodes.length == 0) {
            clearInterval(treeAsyncReady.timer);
            treeAsyncReady.timer == null;
            
            // 执行函数
            f();
            
            treeAsyncReady.done = true;
        }
    }, 13);
}

实现搜索功能

定义了这个方法,然后就可以在这个方法实现实现你要搜索的功能了。

treeAsyncReady('treeId', function() {
    // 在这里写搜索节点的代码
    ...
});

搜索节点

搜索节点的功能要求根据关键字模糊匹配节点,并且要展开节点所在的父节点直到根节点。

function swarchNodes(treeId, keyword) {
    var zTreeObj = $.fn.zTree.getZTreeObj();
    
    // 去掉上一次查询到的节点颜色,如果是第一次搜索则不用
    if (searchNodes.nodes) {
        var prevNodes = searchNodes.nodes;
        for (var i = 0; i < prevNodes.length; i++) {
            prevNodes[i].highlight = false;
            zTreeObj.updateNode(prevNodes[i]);
        }
        // 收起全部节点
        zTreeObj.expandAll(false);
    }
    
    // 重新查找节点
    var nodes = zTreeObj.getNodesByParamFuzzy('name', $.trim(keyword));
    for (var i = 0; i < nodes.length; i++) {
        nodes[i].highlight = true;
        zTreeObj.updateNode(nodes[i]);
        
        // 判断是否是根节点,如果是根节点则不用展开
        if (nodes[i].level != 0) {
            // 自己定义的一个查找全部父节点的方法
            var parentNodes = getParentNodes(treeId, nodes[i]);
            // **这里主要展开节点时必须先从根节点开始展开,否则会出问题**
            for (var j = parentNodes.length - 1; j >= 0; j--) {
                // 展开没有展开的父节点
                if (!parentNodes[j].open) {
                    zTreeObj.expandNode(parentNodes[j], true, false, false);
                }
            }
        }
    }
    
    // 缓查找到的节点
    searchNodes.nodes = nodes;
}

查找全部父节点

这个方法是查找节点的全部父节点,直到根父节点。

function getParentNodes(treeId, node, cache) {
    // 缓存变量
    cache = cache || [];
    var zTreeObj = $.fn.zTree.getZTreeObj();
    var parentNode = node.getParentNode();
    // 有父节点则缓存,直到根节点
    if (parentNode != null) {
        cache.push(parentNode);
    } else {
        return cache;
    }
    // 递归调用查找父节点
    return getParentNodes(treeId, parentNode, cache);
}

naou
11 声望0 粉丝