【问题标题】:How to create Tree with checkboxes using JSON data with Parent Child Relation?如何使用带有父子关系的 JSON 数据创建带有复选框的树?
【发布时间】:2014-11-09 10:39:38
【问题描述】:

我有 JSON 数据并且 JSON 数据有父子关系。我想从中创建树结构。我找到了很多插件和库,但我找不到我的要求。我正在使用 PHP 脚本获取此 JSON 数据。

这是我想要创建的具有树形结构的图像。我被困住了。我知道 JSON 不像图像中显示的那样,但我只想向您展示一棵树应该是什么样子。如何创建像图像中一样的树。我想要的是处理和创建这种类型的 JavaScript 代码树的结构。工作示例是必须的,非常感谢。

您可以随意使用 JSON 格式,并且树应该是可折叠的。还为其提供所需的 JSON 格式。

和我的JSON数据如下:

     {
     "2":
         {
          "5": "Wrist Watch"
         },
     "5":
         {
          "9": "Men's"
         },
     "18": 
         {
         "3": "Clothing"
         },
     "28": 
         {
         "1": "Perfumes"
         },
     "29": 
         {
         "7": "Laptop",
         "10": "Tablets"
         },
     "30":
         {
          "8": "Mobile"
         },
    "31": 
         {
          "2": "Books"
         },
    "33": 
         {
          "6": "Electronics"
         },
   "34": 
         {
          "4": "Home & Kitchen\n"
         }
    }

【问题讨论】:

  • 您的 JSON 没有定义顺序 - 如果顺序很重要,它应该使用数组而不是对象。此外,容器没有使用此结构定义的标签。
  • 您好,在我回答这个问题之前有一个小问题:您是否需要搜索对象?我可以向您展示一种设置目录类的方法,以便您可以通过更面向对象的方式添加子目录等
  • @MaxBumaye 你能告诉我你的答案吗?
  • 用那里的目录类查看我的答案 - 是不是您正在寻找的东西?

标签: javascript jquery html json tree


【解决方案1】:

如果你想自己滚动,“trees”中的关键字是recursion。它需要支持任何深度的数据并且代码和数据应该支持递归。

这意味着您的 JSON 数据应该是一个递归结构,其中每个节点看起来都一样(看起来像这样):

{
    id: 1,                  // data id
    title: "title",         // display title
    children: [             // list of children, each with this same structure
        // list of child nodes
    ]
}

注意:我已更改样本数据以包含更多深度,因为 2 级永远不会出现递归问题。

例如:

{
    id: 0,
    title: "root - not displayed",
    children: [{
        id: 1,
        title: "Option 1",
        children: [{
            id: 11,
            title: "Option 11",
            children: [{
                id: 111,
                title: "Option 111"
            }, {
                id: 112,
                title: "Option 112"
            }]
        }, {
            id: 12,
            title: "Option 12"
        }]
    }, {
        id: 2,
        title: "Option 2",
        children: [{
            id: 21,
            title: "Option 21"
        }, {
            id: 22,
            title: "Option 22"
        }]
    }, {
        id: 3,
        title: "Option 3",
        children: [{
            id: 31,
            title: "Option 31"
        }, {
            id: 32,
            title: "Option 32"
        }]
    }]
}

递归函数如下所示:

function addItem(parentUL, branch) {
    for (var key in branch.children) {
        var item = branch.children[key];
        $item = $('<li>', {
            id: "item" + item.id
        });
        $item.append($('<input>', {
            type: "checkbox",
            name: "item" + item.id
        }));
        $item.append($('<label>', {
            for: "item" + item.id,
            text: item.title
        }));
        parentUL.append($item);
        if (item.children) {
            var $ul = $('<ul>').appendTo($item);
            addItem($ul, item);
        }
    }
}

JSFiddle: http://jsfiddle.net/0s0p3716/188/

代码递归结构,添加新的 UL 和 LI(带有复选框等)。顶层调用只是提供了显示和数据的初始根起点。

addItem($('#root'), data);

最终结果如下所示:

如果您想根据选中状态切换可见性,请使用:

$(':checkbox').change(function () {
    $(this).closest('li').children('ul').slideToggle();
});

如果您还希望标签切换复选框,请使用:

$('label').click(function(){
    $(this).closest('li').find(':checkbox').trigger('click');
});

注意:我只提供了最基本的样式,因为这通常是“品尝”。链接中的示例显示在另一个答案中。

--更新:

修正:第 31 和 32 项的 ID 可能有误?

更好的选择和取消选择功能(用于级联到子节点的父节点):

$(function () {
addItem($('#root'), data);
$(':checkbox').click(function () {
    $(this).find(':checkbox').trigger('click');
    var matchingId = $(this).attr('id');
    if ($(this).attr('checked'))
    {
        $('input[id*=' + matchingId +']').each(function() {
        $(this).removeAttr('checked');
            $(this).prop('checked', $(this).attr('checked'));
        });
    }
    else {
         $('input[id*=' + matchingId +']').each(function() {
        $(this).attr('checked', 'checked');
            $(this).prop('checked', $(this).attr('checked'));

         });
    }

});
$('label').click(function(){
    $(this).closest('li').children('ul').slideToggle();

});

-- 用这里显示的更新小提琴(JsFiddle),它会更好地工作,并且还允许您单击文本以展开而不同时选择 - 我知道我发现这更有用。这将有助于(这是个人偏好)您无需选择第一个即可查看可用的值和选项。

【讨论】:

  • 感谢您的完美回答,需要另一个帮助。我有来自 php 代码的 JSON 编码数据。 var data = '';当我这样做时,这不会给我树 $str 是您指定的 json 数据和格式。我的代码有什么问题?
  • @Deep Shah:使用 Fiddler2 检查请求和响应,它将直观地解码响应,以便您验证它。如果这没有帮助,请创建一个新问题(带有完整代码)并在此处添加一个链接:)
  • 你的代码有小问题。关闭父节点后,子复选框不会取消选中。你能更新你的代码吗?
  • 菜鸟问题:$item.append(); 在这里服务的目的是什么?
  • @dtgq:目的是看看是否有人真正阅读过代码(开个玩笑)。在我改用 appendTo 之前,它只是从早期的代码编辑中遗留下来的。我已将其删除并更新了 Fiddle。谢谢:)
【解决方案2】:

编程的问题是:现有的库和工具很少能完全满足您的需求。您始终可以将输入数据转换为他们期望的格式,然后将输出数据转换为您需要的格式。有时,这种转换比编写自己的代码而不是库函数需要更多的努力——这似乎是其中一种情况。

正如@philosophocat 已经指出的那样,在 HTML 标记中呈现这种树的最佳方式是嵌套列表。您只需要递归地遍历 JSON 数据并创建相应的元素:

function createList(data)
{
  var result = document.createElement("ul");
  for (var key in data)
  {
    if (!data.hasOwnProperty(key) || key == "_title")
      continue;
    var value = data[key];
    var item = createItem(key, typeof value == "string" ? value : value._title);
    if (typeof value == "object")
      item.appendChild(createList(value));
    result.appendChild(item);
  }
  return result;
}

function createItem(value, title)
{
  var result = document.createElement("li");
  var checkbox = document.createElement("input");
  checkbox.setAttribute("type", "checkbox");
  checkbox.setAttribute("name", "selection");
  checkbox.setAttribute("value", value);
  result.appendChild(checkbox);
  result.appendChild(document.createTextNode(title));
  return result;
}

document.body.appendChild(createList(jsonData));

请注意,项目出现的顺序在这里是“随机的”,因为对象键通常是无序的。您可以更改上面的代码以某种方式对键进行排序,或者您可以更改数据以使用数组并定义顺序。我还在数据中添加了"_title" 属性,以确保已标记类别 - 您的数据根本没有任何类别标签。

现在您需要对列表进行样式设置,使其看起来像一棵树。显而易见的解决方案是使用list-style-image CSS property 将通常的项目符号点替换为网格线图像。但是,这不适用于嵌套列表 - 您需要显示多个图像、来自更高级别列表的垂直线以及实际属于当前列表项的图像。

这可以通过使用列表项的背景图像来解决,这些背景图像也会显示在子列表旁边。以下是我得到的示例样式:

ul
{
  padding: 0;
  list-style-type: none;
  font-size: 14px;
}

li
{
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAABkCAYAAABdELruAAAAP0lEQVR42u3PQQoAIAgEQP3/o6t7JAhdolkQD4sMZuwZazKKlGXniHRDOu6HfyKRSCQSiUQikUgkEolEIv0rTc/fNmQ78+lPAAAAAElFTkSuQmCC);
  background-repeat: no-repeat;
  padding-left: 13px;
}

li:last-child
{
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAJCAYAAADpeqZqAAAAHUlEQVR42mNkwAT/gZiRAQ/AK0mKplGbqGETThoACFgJCVdBEqAAAAAASUVORK5CYII=);
}

li > ul
{
  margin-left: 5px;
}

请注意,如果子列表太高,这仍然会变得很丑 - 我使用的背景图像的高度仅为 100 像素。当然,这可以通过使用更大的背景图像来解决。更简洁的替代方法是使用 border-image-slice CSS property,但目前仅在 Firefox 中支持。

Fiddle for this code

编辑This article 更详细地介绍了嵌套列表的样式,如树。虽然方法类似,但它设法避免了我上面提到的图像大小问题,方法是使用可以垂直重复的垂直线的单独图像。不利的一面是,这种方法看起来可能只适用于实线,如果应用于虚线则会产生伪影。

【讨论】:

    【解决方案3】:

    使用http://www.jstree.com/。这个库提供了我在使用树和 javascript 时需要的每个功能。

    您只需根据给定的格式(http://www.jstree.com/docs/json/)更改您的 json-response:

    {
      id          : "string" // will be autogenerated if omitted
      text        : "string" // node text
      icon        : "string" // string for custom
      state       : {
        opened    : boolean  // is the node open
        disabled  : boolean  // is the node disabled
        selected  : boolean  // is the node selected
      },
      children    : []  // array of strings or objects
      li_attr     : {}  // attributes for the generated LI node
      a_attr      : {}  // attributes for the generated A node
    }
    

    设置 javascript 并包含所有必需的文件,然后就可以了。 我只是通过引用它来跳过重复文档:http://www.jstree.com/

    【讨论】:

      【解决方案4】:

      我正在将 DynaTree 用于工作中的内部网站,效果非常好。

      1. 下载DynaTree
      2. 这样格式化您的 JSON(以您的屏幕截图为例):
      { "title": "运动与户外", “isFolder”:是的, “关键”:“0”, “孩子们”: [ { "title": "健身配件", “关键”:“1”, “isFolder”:是的, “孩子们”: [ { "title": "健身配件", “关键”:“2”, “isFolder”:是的, “孩子们”: [ { "title": "计步器和手表", “钥匙”:“3” } ] } ] } ] }
      1. 在页面加载时运行这个 JS:
      $("#buildTree").dynatree({ onActivate:函数(节点){ // 一个 DynaTreeNode 对象被传递给激活处理程序 // 注意:如果持久性打开并且页面重新加载,我们也会收到此事件。 leftActiveNodeKey = node.data.key; }, 坚持:假, 复选框:真, 选择模式:3, 孩子:$.parseJSON(response.d) });
      1. 要获取您可以使用的选定节点:

        var selectedNodes = $("#buildTree").dynatree("getTree").getSelectedNodes();

      Dynatree 在外观和功能上都非常可定制。通读文档以了解您需要的设置。

      【讨论】:

        【解决方案5】:
        【解决方案6】:

        @Gone Coding 的示例非常好,但子复选框不会显示为“未选中”,即使已删除选中的属性,就像在 Chrome 中呈现的那样。 如果添加,

        $(this).prop('checked', false);
        

        到代码,所以它读作

        $(function () {
            addItem($('#root'), data);
            $(':checkbox').click(function () {
                var matchingId = $(this).attr('id');
                if ($(this).attr('checked'))
                {
                    $('input[id*=' + matchingId +']').each(function() {
                    $(this).removeAttr('checked');
                    $(this).prop('checked', false);
                        $(this).prop('checked', $(this).attr('checked'));
                        return;
                    });
                }
                else {
                     $('input[id*=' + matchingId +']').each(function() {
                    $(this).attr('checked', 'checked');
                        $(this).prop('checked', $(this).attr('checked'));
                     });
                }
        
            });
            $('label').click(function(){
                $(this).closest('li').children('ul').slideToggle();
        
            });
        });
        

        当用户进行更改时,子复选框将填充或清除。

        【讨论】:

          猜你喜欢
          • 2018-06-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-06
          • 1970-01-01
          • 2011-07-03
          • 1970-01-01
          • 2016-07-12
          相关资源
          最近更新 更多