简体   繁体   English

遍历对象和未知数量的孩子

[英]Loop through object and unknown amount of children

I need to loop through the following object and run a function for each child. 我需要遍历以下对象并为每个孩子运行一个函数。 Basically I want to generate HTML from the object, so children within the object will obviously be children within the HTML. 基本上,我想从对象生成HTML,因此对象内的子级显然将是HTML内的子级。

So, the object: 因此,对象:

var html = {
  'div' : {
    'id': 'marvLightbox__container',
    0: {
      'div': {
        'class': 'marvLightbox__left',
        'event_click': 'left'
      }
    },
    1: {
      'div': {
        'class': 'marvLightbox__right',
        'event_click': 'right'
      }
    },
    2: {
      'div': {
        'class': 'marvLightbox',
        0: {
          'div': {
            'class': 'marvLightbox__eschint',
            'content': 'Press <span>ESC</span> to close'
          },
          'div': {
            'class': 'marvLightbox__close',
            'event_click': 'close'
          },
          'img': {
            'src': '/img-src/_themev2-knightsbridgecars-1544/making-of/making-004.jpg',
            'class': 'responsive-img'
          }
        }
      }
    }
  }
}

The above object should generate the following, if we ignore the event_click sections for now: 如果我们event_click忽略event_click部分,则上述对象应生成以下内容:

<div id="marvLightbox__container">
  <div class="marvLightbox__left"></div>
  <div class="marvLightbox__right"></div>
  <div class="marvLightbox">
    <div class="marvLightbox__eschint">
      Press <span>ESC</span> to close
    </div>
    <div class="marvLightbox__close"></div>
    <img src="/img-src/_themev2-knightsbridgecars-1544/making-of/making-004.jpg" alt="" class="responsive-img">
  </div>
</div>

This is the code I currently have, however it doesn't go deeper into the object and it outputs incorrectly. 这是我当前拥有的代码,但是并没有深入到对象中,并且输出不正确。

Object.size = function(obj) {
  var size = 0, key;
  for (key in obj) {
    if (obj.hasOwnProperty(key)) size++;
  }
  return size;
};
function allDescendants (node, n) {
  var i = 0;
  for (var property in node) {
    var child = node[property];
    console.log(property);
    if (Object.size(child) > 1) {
      allDescendants(child, i);
    }
    i++;
    //allDescendants(child, i);
    doSomethingToNode(child, i);
  }
}
function doSomethingToNode(node, n) {
  console.log(n + ': ' + node);
}
allDescendants(html);

This is the what the above code currently outputs, I will put a Codepen below too so you can test easier: 这是以上代码当前输出的内容,我也将Codepen放在下面,以便您可以更轻松地进行测试:

marv.lightbox.js:141 div
marv.lightbox.js:141 0
marv.lightbox.js:151 1
marv.lightbox.js:152 Object {div: Object}
marv.lightbox.js:141 1
marv.lightbox.js:151 2
marv.lightbox.js:152 Object {div: Object}
marv.lightbox.js:141 2
marv.lightbox.js:151 3
marv.lightbox.js:152 Object {div: Object}
marv.lightbox.js:141 id
marv.lightbox.js:141 0
marv.lightbox.js:151 1
marv.lightbox.js:152 m
marv.lightbox.js:141 1
marv.lightbox.js:151 2
marv.lightbox.js:152 a
marv.lightbox.js:141 2
marv.lightbox.js:151 3
marv.lightbox.js:152 r
marv.lightbox.js:141 3
marv.lightbox.js:151 4
marv.lightbox.js:152 v
marv.lightbox.js:141 4
marv.lightbox.js:151 5
marv.lightbox.js:152 L
marv.lightbox.js:141 5
marv.lightbox.js:151 6
marv.lightbox.js:152 i
marv.lightbox.js:141 6
marv.lightbox.js:151 7
marv.lightbox.js:152 g
marv.lightbox.js:141 7
marv.lightbox.js:151 8
marv.lightbox.js:152 h
marv.lightbox.js:141 8
marv.lightbox.js:151 9
marv.lightbox.js:152 t
marv.lightbox.js:141 9
marv.lightbox.js:151 10
marv.lightbox.js:152 b
marv.lightbox.js:141 10
marv.lightbox.js:151 11
marv.lightbox.js:152 o
marv.lightbox.js:141 11
marv.lightbox.js:151 12
marv.lightbox.js:152 x
marv.lightbox.js:141 12
marv.lightbox.js:151 13
marv.lightbox.js:152 _
marv.lightbox.js:141 13
marv.lightbox.js:151 14
marv.lightbox.js:152 _
marv.lightbox.js:141 14
marv.lightbox.js:151 15
marv.lightbox.js:152 c
marv.lightbox.js:141 15
marv.lightbox.js:151 16
marv.lightbox.js:152 o
marv.lightbox.js:141 16
marv.lightbox.js:151 17
marv.lightbox.js:152 n
marv.lightbox.js:141 17
marv.lightbox.js:151 18
marv.lightbox.js:152 t
marv.lightbox.js:141 18
marv.lightbox.js:151 19
marv.lightbox.js:152 a
marv.lightbox.js:141 19
marv.lightbox.js:151 20
marv.lightbox.js:152 i
marv.lightbox.js:141 20
marv.lightbox.js:151 21
marv.lightbox.js:152 n
marv.lightbox.js:141 21
marv.lightbox.js:151 22
marv.lightbox.js:152 e
marv.lightbox.js:141 22
marv.lightbox.js:151 23
marv.lightbox.js:152 r
marv.lightbox.js:151 4
marv.lightbox.js:152 marvLightbox__container
marv.lightbox.js:151 1
marv.lightbox.js:152 Object {0: Object, 1: Object, 2: Object, id: "marvLightbox__container"}

Codepen example Codepen示例

First, I would consider restructuring your data to utilize arrays, which will make traversing much easier. 首先,我将考虑重组您的数据以利用数组,这将使遍历变得更加容易。

var html = {
  'id': 'marvLightbox__container',
  'children' : [
    {
      'class': 'marvLightbox__left',
      'event_click': 'left'
    },
    {
      'class': 'marvLightbox__right',
      'event_click': 'right'
    },
    {
      'class': 'marvLightbox',
      'children': [
        {
          'class': 'marvLightbox__eschint',
          'content': 'Press <span>ESC</span> to close'
        },
         {
          'class': 'marvLightbox__close',
          'event_click': 'close'
        },
        {
          'src': '/img-src/_themev2-knightsbridgecars-1544/making-of/making-004.jpg',
          'class': 'responsive-img'
        }
      ]
    }
  ]
}

Then you can walk the entire object like so: 然后,您可以像这样遍历整个对象:

//Recursively loop through all children
function walkTheObject(dataNode, func) {
  func(dataNode);

  if(dataNode.children) {
    dataNode.children.forEach((child) => {
      walkTheObject(child, func)
    })
  }
}

Assuming you can start with a JSON string instead, you can use JSON.parse() with a custom reviver function to make things easier: 假设您可以改用JSON字符串开头,则可以将JSON.parse()与自定义的reviver函数结合使用,以使事情变得更容易:

 var htmlJson = `{ "div": { "0": { "div": { "class": "marvLightbox__left", "event_click": "left" } }, "1": { "div": { "class": "marvLightbox__right", "event_click": "right" } }, "2": { "div": { "0": { "div": { "class": "marvLightbox__eschint", "content": "Press <span>ESC</span> to close" } }, "1": { "div": { "class": "marvLightbox__close", "event_click": "close" } }, "2": { "img": { "src": "/img-src/_themev2-knightsbridgecars-1544/making-of/making-004.jpg", "class": "responsive-img" } }, "class": "marvLightbox" } }, "id": "marvLightbox__container" } }`; function handleAttribute(element, attribute, value) { if (value instanceof HTMLElement) { return element.appendChild(value); } switch (attribute) { case 'class': case 'src': case 'id': return element.setAttribute(attribute, value); case 'content': return element.innerHTML = value; // other keys... default: console.log(element.tagName, attribute, value); } } function htmlReviver(key, value) { // parse as element if (isNaN(key) && typeof value === 'object') { var element = document.createElement(key); var subValue; for (var attribute in value) { handleAttribute(element, attribute, value[attribute]); } return element; // move element from { index: { tagName: Element } } to { index: Element } } else if (!isNaN(key)) { return value[Object.keys(value)[0]]; // leave property alone } else { return value; } } var htmlObject = JSON.parse(htmlJson, htmlReviver); console.log(htmlObject); document.body.appendChild(htmlObject); 

First of all your object in this structure will not work, because you have a duplicate properties in the same object, like this: 首先,此结构中的对象将无法工作,因为在同一对象中具有重复的属性,如下所示:

This piece from your code i just put it in x variable. 我将这段代码放在x变量中。

var x = {
     'div': {
        'class': 'marvLightbox__eschint',
        'content': 'Press <span>ESC</span> to close'
      },
      'div': {
        'class': 'marvLightbox__close',
        'event_click': 'close'
      }
   }

the duplicate propriety div will overwritten. 重复的div将被覆盖。 so this will be converted to be : 所以这将被转换为:

var x = {
      'div': {
        'class': 'marvLightbox__close',
        'event_click': 'close'
      }
   }

So You need to change your object structure to be better than the current. 因此,您需要更改object结构以使其优于当前结构。

for this you need to use Array , this will help you to duplicate elements in the same level without any problems. 为此,您需要使用Array ,这将帮助您在相同级别中复制元素而没有任何问题。

I made a simple structure and implement it for your object, you can use another one structure but still need to change the current structure. 我做了一个简单的结构并为您的对象实现了它,您可以使用另一种结构,但仍然需要更改当前结构。

This code === your current code but in other structure 此代码===您当前的代码,但采用其他结构

var html = [{
   "tag": "div",
   "attributes": {
    "id": "marvLightbox__container"
  },
  "text": "",
  "children": [
    {
        "tag": "div",
        "attributes": {
            "class": "marvLightbox__left",
            "event_click": "left"
        },
        "text": "",
        "children": []
    },
    {
        "tag": "div",
        "attributes": {
            "class": "marvLightbox__right",
            "event_click": "right"
        },
        "text": "",
        "children": []
    },
    {
        "tag": "div",
        "attributes": {
            "class": "marvLightbox"
        },
        "text": "",
        "children": [
            {
                "tag": "div",
                "attributes": {
                    "class": "marvLightbox__eschint"
                },
                "text": "Press <span>ESC</span> to close",
                "children": []
            },
            {
                "tag": "div",
                "attributes": {
                    "class": "marvLightbox__eschint"
                },
                "text": "Press <span>ESC</span> to close",
                "children": []
            },
            {
                "tag": "div",
                "attributes": {
                    "class": "marvLightbox__close",
                    "event_click": "close"
                },
                "text": "",
                "children": []
            },
            {
                "tag": "img",
                "attributes": {
                    "src": "/img-src/_themev2-knightsbridgecars-1544/making-of/making-004.jpg",
                    "class": "responsive-img"
                },
                "text": "",
                "children": []
            }
        ]
    }
  ]
}];

Now you have a nice structure for your object. 现在,您的对象有了一个不错的结构。 all you need is to make a function that's build HTML string for 1 element. 您需要做的是为1个元素构建HTML字符串的函数。 then call it for all nested elements using technique called Recursion (to call the function in inside itself) 然后使用称为递归的技术为所有嵌套元素调用它(以在内部调用该函数)

This is an example for your case, using Object.keys() and Array.prototype.reduce() 这是您的示例,使用Object.keys()Array.prototype.reduce()

 // Your code in another format var html = [{ "tag": "div", "attributes": { "id": "marvLightbox__container" }, "text": "", "children": [ { "tag": "div", "attributes": { "class": "marvLightbox__left" }, "text": "", "children": [] }, { "tag": "div", "attributes": { "class": "marvLightbox__right" }, "text": "", "children": [] }, { "tag": "div", "attributes": { "class": "marvLightbox" }, "text": "", "children": [ { "tag": "div", "attributes": { "class": "marvLightbox__eschint" }, "text": "Press <span>ESC</span> to close", "children": [] }, { "tag": "div", "attributes": { "class": "marvLightbox__close" }, "text": "", "children": [] }, { "tag": "img", "attributes": { "src": "/img-src/_themev2-knightsbridgecars-1544/making-of/making-004.jpg", "class": "responsive-img" }, "text": "", "children": [] } ] } ] }]; // Our main function + helper var for tags that not need to cloded like input and img var noClosingTags = ["img"]; function buildHtmlTag(arr) { "use strict"; if (typeof arr !== 'object') { console.error(arr, ' Should be array or object'); return; } arr = arr instanceof Array ? arr : [arr]; return arr.reduce(function (acc, item) { var attributes = Object.keys(item.attributes).reduce(function (acc, key) { return acc + key + "=\\"" + item.attributes[key] + "\\" "; }, ""); acc += noClosingTags.indexOf(item.tag) > -1 ? "<" + item.tag + " " + attributes + "/>" : "<" + item.tag + " " + attributes + ">"; acc += item.text ; acc += buildHtmlTag(item.children); acc += noClosingTags.indexOf(item.tag) > -1 ? "" : "</" + item.tag + ">"; return acc; }, ""); } // Test var result = buildHtmlTag(html); console.log(result); document.getElementById('main').innerHTML = result; 
 <div id="main"><div> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM