У меня есть объект JSON, который я использую для построения дерева с использованием списков. Объект JSON имеет следующую структуру:
const data = [
{ "name": "Node 1", "children":
[
{ "name": "Node 1.1", "children":
[
{ "name": "Node 1.1.1", "children": [], "leaf": true },
{ "name": "Node 1.1.2", "children": [], "leaf": true }
]
},
{ "name": "Node 1.2", "children":
[ { "name": "Node 1.2.1", "children": [], "leaf": true } ]
}
]
},
{ "name": "Node 2", "children":
[
{ "name": "Node 2.1", "children": [] },
{ "name": "Node 2.2", "children":
[ { "name": "Node 2.2.1", "children": [], "leaf": true } ]
}
]
},
{ "name": "Node 3", "children": [] }
];
Этот объект затем используется в функции createTree
;
createTree(nodes, container)
{
const list = document.createElement('ul');
nodes.forEach((node) => {
const listItem = document.createElement('li');
listItem.textContent = node.name;
list.appendChild(listItem);
if (node.children) {
const childList = document.createElement('li');
this.createTree(node.children, childList);
list.appendChild(childList);
}
});
container.appendChild(list);
}
nodes
изначально является объектом data
, container
- это элемент для размещения дерева. Эта функция создает следующий вид дерева;
<ul>
<li>Node 1</li>
<li>
<ul>
<li>Node 1.1</li>
<li>
<ul>
<li>Node 1.1.1</li>
<li>
<ul></ul>
</li>
<li>Node 1.1.2</li>
<li>
<ul></ul>
</li>
</ul>
</li>
<li>Node 1.2</li>
<li>
<ul>
<li>Node 1.2.1</li>
<li>
<ul></ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Node 2</li>
<li>
<ul>
<li>Node 2.1</li>
<li>
<ul></ul>
</li>
<li>Node 2.2</li>
<li>
<ul>
<li>Node 2.2.1</li>
<li>
<ul></ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Node 3</li>
<li>
<ul></ul>
</li>
</ul>
Это почти так, как я хочу, есть много ненужных тегов li
и ul
, но поскольку я использую list-style-type: none
, это не имеет значения, ненужные элементы сворачиваются и не отображаются.
Я также добавил поле ввода, используемое для фильтрации по дереву и отображения только соответствующих листьев (отсюда и значение leaf
в объекте JSON). Это должны показать только соответствующие листья, но и их родители. Мне удалось частично сделать это с помощью следующей функции фильтра:
$('#input').on('keyup', () => {
const value = $('#input').val().toLowerCase().trim();
const nodes = oldData.filter(function f(node) {
if (node.name.toLowerCase().includes(value)) return true;
if (node.children) {
return (node.children = node.children.filter(f)).length;
}
});
// Simply removes all children from the root element so there's only one tree, instead of the new tree being stacked on top of the new one
this.removeTree();
// Rebuild the new tree
this.createTree(nodes, document.getElementById('root-element'));
});
Это работает в том смысле, что оно показывает только соответствующие листья, включая их родительские узлы, но если вы очистите поле ввода, будут видны только элементы, видимые после поискового запроса. Я знаю, почему это происходит (функция filter
удаляет все несоответствия), но я не знаю, как этого избежать. Я пытался сохранить начальное состояние data
в переменной за пределами прослушивателя событий keyup
и установить nodes
в эту переменную в тот момент, когда поле ввода пустое, но, похоже, это не помогает.
Я также пытался пройтись по фактическому дереву HTML, но я даже не могу должным образом получить доступ к дочерним узлам узлов 1, 2 и 3, поэтому я тоже в растерянности.
Другими словами, каков наилучший способ отфильтровать листья дерева, одновременно показывая их родителей и восстановить оригинал после того, как фильтр опустеет?
Использование jQuery.grep()
;
const nodes = $.grep(data, function f(node) {
if (node.name.toLowerCase().includes(value)) return true;
});