Фильтровать через вложенную структуру ul / li с полем ввода, сбросить в исходное состояние, если вход пуст - PullRequest
1 голос
/ 26 июня 2019

У меня есть объект 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;
});

1 Ответ

1 голос
/ 26 июня 2019

Насколько я понимаю, у вас было 2 проблемы.

  1. Способ, которым вы копируете объект. Когда вы пишете let oldData = data;, вы получаете ссылку на исходный объект данных, а не на другой объект. Есть много способов скопировать объекты, которые я использовал через JSON.
$(document).ready(() => {
  createTree(data, $('#container'));
  $('#asdf').on('keyup', () => {
    let dataCopy = JSON.parse(JSON.stringify(data));

    const value = $('#asdf').val().toLowerCase().trim();
    let nodes = {};
    if (!value) { nodes = data; } else {
      nodes = filterData(dataCopy, value);
    }

    // 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, $('#container'));
  });
});
  1. Внутри функции фильтра вы должны проверить, возвращались ли какие-либо дочерние узлы.
function filterData(data, name) {
  return data.filter(function(o) {
    if (o.children) o.children = filterData(o.children, name);
    return o.name.toLowerCase().includes(name) || o.children.length;
  });
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...