現在在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
方法的具體的含義,並按需改造成適合自己使用的場景。
<全文完>