如何使用带有父子关系的JSON数据创建带有复选框的树?

时间:2014-11-09 10:39:38

标签: javascript jquery html json tree

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

这是具有我想要创建的树结构的图像。我坚持认为它。我知道JSON不是在图像中显示,但我只想告诉你树应该是什么样子。如何在图像中创建树。我想要的是javascript代码来处理和创建这种树的结构。工作范例必须&非常感谢。

您可以根据需要使用JSON格式,树应该是可折叠的。还要为它提供所需的JSON格式。

enter image description here

和我的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"
         }
    }

6 个答案:

答案 0 :(得分:8)

如果你想自己滚动,那么"树"中的关键字是递归。它需要支持任何深度的数据,代码和数据支持递归。

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

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

注意:我已将示例数据更改为包含更多深度,因为2个级别从不显示递归问题。

e.g:

{
    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);

最终结果如下:

enter image description here

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

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

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

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

注意:我只提供了最基本的造型,因为它通常是&#34;品尝&#34;。链接中的示例显示在另一个答案中。

- 更新:

  

修正:项目31&amp;的可能错误的ID 32?

更好地选择和取消选择的功能(对于父级连接到子节点):

$(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),它会更好用,也可以让你点击文字展开而不同时选择 - 我知道我发现这个更多有用。它将有助于(,这是个人偏好),您可以看到哪些值和选项可用,而无需选择第一个。

答案 1 :(得分: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));

请注意,项目的显示顺序为&#34;随机&#34;这里,对象键通常是无序的。您可以更改上面的代码以某种方式对键进行排序,或者您可以更改数据以使用数组并定义顺序。我还在数据中添加了"_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;
}

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

Fiddle for this code

编辑This article详细介绍了如树的样式化嵌套列表。虽然方法类似,但它设法通过使用垂直线的单独图像来避免上面提到的图像尺寸问题,该图像可以垂直重复。在缺点方面,这种方法看起来可能只适用于实线,如果应用于虚线则会产生伪影。

答案 2 :(得分:1)

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

您必须根据给定的格式(http://www.jstree.com/docs/json/)更改您的json响应:

{
  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/

答案 3 :(得分:0)

我正在使用DynaTree作为工作中的内部网站,它的工作非常棒。

  1. 下载DynaTree
  2. 如此格式化您的JSON(以屏幕截图为例):
  3. {
      "title": "Sports & Outdoors",
      "isFolder": true,
      "key": "0",
      "children": [
            {
              "title": "Fitness Accessories",
              "key": "1",
              "isFolder": true,
              "children": [
                {
                  "title": "Fitness Accessories",
                  "key": "2",
                  "isFolder": true,
                  "children": [
                    {
                      "title": "Pedometer & Watches",
                      "key": "3" 
                    }
                  ] 
                }
              ]
            }
          ]
    }
    1. 在页面加载时运行此JS:
    2.     $("#buildTree").dynatree({
            onActivate: function (node) {
              // A DynaTreeNode object is passed to the activation handler
              // Note: we also get this event, if persistence is on, and the page is reloaded.
              leftActiveNodeKey = node.data.key;
            },
            persist: false,
            checkbox: true,
            selectMode: 3,
            children: $.parseJSON(response.d)
          });
      
      
      1. 要获取所选节点,您可以使用:

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

      2. Dynatree非常适合外观和功能。阅读文档以了解所需的设置。

答案 4 :(得分:0)

答案 5 :(得分:0)

@Gone Coding的例子非常好,但是子复选框不会显示为“未编辑的”&#39;即使已删除已检查的属性,也会在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();

    });
});

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