通过带有输入字段的嵌套ul / li结构进行过滤,如果输入为空,则重置为原始状态

时间:2019-06-26 12:39:50

标签: javascript html arrays filter

我有一个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>

这几乎是我想要的方式,有很多不必要的liul标签,但是因为我使用的是list-style-type: none,所以没有必要,不必要的元素是折叠且未显示。

我还添加了一个输入字段,用于过滤树并仅显示相关的叶子(因此JSON对象中的leaf值)。它应该仅显示相关的叶子,还应显示其父母。我已经通过以下过滤器功能部分完成了此操作;

$('#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 个答案:

答案 0 :(得分:1)

据我了解,您有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;
  });
}