现在在2020年了,jonmiles/bootstrap-treeview 项目已经归档了,并且最后一次更新在2015年。但是,项目中使用到了这个库,所以,没得选择,只能粪不顾身跳入坑里。
这篇文章主要吐槽bootstrap-treeview的两个方法:checkNode
和 expandNode
的使用。
checkNode 方法
顾名思义,这个方法用来勾选 node 节点。根据文档说明:
checkNode
方法的第一个参数可以是 node 对象或者 nodeId 数字。这里要特别注意 nodeId 数字,并不是想当然的初始化 treeview 所传入的 data 属性中对象的 id 字段。原先我也先入为主的以为是 data 对象中的 id 字段,但是发现不是,于是阅读了源代码。
/*
Identifies a node from either a node id or object
*/
Tree.prototype.identifyNode = function (identifier) {
return ((typeof identifier) === 'number') ?
this.nodes[identifier] :
identifier;
};
identifyNode
方法,用来通过 identifier 标识来返回 node 对象。如果 identifier 是数字,就返回 this.nodes[identifier],否则原样返回。
由于要找的是 identifier 是数字时所代表的含义,找到 setInitialStates
方法,它用来初始化所有节点及节点的状态:
Tree.prototype.setInitialStates = function (node, level) {
if (!node.nodes) return;
level += 1;
var parent = node;
var _this = this;
$.each(node.nodes, function checkStates(index, node) {
// nodeId : unique, incremental identifier
node.nodeId = _this.nodes.length;
// 中间代码省略...
// index nodes in a flattened structure for use later
_this.nodes.push(node);
// recurse child nodes and transverse the tree
if (node.nodes) {
_this.setInitialStates(node, level);
}
});
};
从这两行关键代码可以看出来 nodeId 是从 0 开始的自增的整数,而并非 data 对象中的 id 字段:
// nodeId : unique, incremental identifier
node.nodeId = _this.nodes.length;
// 中间代码省略...
// index nodes in a flattened structure for use later
_this.nodes.push(node)
啰嗦一大堆,结论就是 nodeId 是 treeview 自动生成的从 0 开始的自增整数。那解决想通过 data 对象中的 id 字段来实现 checkNode 该怎么做呢?
分为两步,要在 bootstrap-treeview 新增一行代码,并且使用这一行新增的代码:
#1
bootstra-treeview.js 的源码里的 var Tree = function () { ... }
最后 return 返回的对象中新增一行代码:
findNodes: $.proxy(this.findNodes, this)
#2
完成 1 步骤后,我们就可以在源码外调用 findNodes
方法。这个方法很强大,可以通过正则表达式查找 nodes 节点中的指定属性的内容,并返回所有匹配的节点,findNodes
的源码:
使用 findNodes
方法,就能够根据 id 属性查找所需的节点,然后传入 checkNodes
方法里:
var node = $('#target').treeview('findNodes', ['^' + n + '$', 'g', 'id']);
if (!node || !node.length) {
return;
}
$('#target').treeview('checkNode', node);
$('#target').treeview('findNodes', ['^' + n + '$', 'g', 'id'])
是含义是查找 nodes 节点中 id 属性值等于变量 n 的所有节点。
expandNode 方法
expandNode
方法用来展开指定的 node 节点,根据文档说明:
expandNode
方法的第一个参数和 checkNode
方法的一样,可以接受 node 对象或者 nodeId 数字。第二个参数可选传入 levels 指定要展开节点的层级。默认就是 1 了,只展开一层;如果 levels: 2
,那么会展开两层。
实际情况中,使用了 checkNode
方法勾选节点后,想要自动展开这些节点,按照 expandNode
方法的实现是完成不了这个需求的。比方说,勾选了第三层级的 C 节点,要实现自动展开这个节点,需要先展开 C 节点的父节点(位于第二层级的某节点),再展开 C 节点的父节点的父节点(位于第一层级的某节点)。
把这个需求用代码实现,方法取名为 expandRealNode
(这才是我心目中的展开节点方法:-D):
#1
在 Tree.prototype.expandNode = function (identifiers, options) { ... }
的后面加上如下代码:
Tree.prototype.expandRealNode = function (identifiers, options) {
this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
var real = node;
while (true) {
real = this.getParent(real);
console.log(real);
if (!real || !real.hasOwnProperty('nodeId')) {
break;
}
this.setExpandedState(real, true, options);
}
}, this));
this.render();
};
#2:
bootstra-treeview.js 的源码里的 var Tree = function () { ... }
最后 return 返回的对象中新增一行代码:
expandRealNode: $.proxy(this.expandRealNode, this)
expandRealNode
的使用方法示例:
var node = $('#target').treeview('findNodes', ['^' + n + '$', 'g', 'id']);
if (!node || !node.length) {
return;
}
$('#target').treeview('checkNode', node);
$('#target').treeview('expandRealNode', node);
总结
主要分析了 bootstrap-treeview 源码里的 checkNode
和 expandNode
方法的具体的含义,并按需改造成适合自己使用的场景。
<全文完>