循环通过对象和未知数量的孩子

时间:2017-06-14 15:59:46

标签: javascript html

我需要遍历以下对象并为每个子项运行一个函数。基本上我想从对象生成HTML,因此对象中的子节点显然是HTML中的子节点。

所以,对象:

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'
          }
        }
      }
    }
  }
}

如果我们暂时忽略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>

这是我目前拥有的代码,但它没有深入到对象中并且输出不正确。

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

这就是上面代码输出的内容,我也会在下面放一个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

3 个答案:

答案 0 :(得分:2)

首先,我会考虑重组您的数据以利用数组,这将使遍历变得更加容易。

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'
        }
      ]
    }
  ]
}

然后你可以像这样走完整个对象:

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

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

答案 1 :(得分:1)

假设您可以使用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);

答案 2 :(得分:1)

首先,此结构中的对象不起作用,因为在同一对象中有重复的属性,如下所示:

我的代码中的这篇文章只是将它放在x变量中。

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

重复的专有权div将被覆盖。所以这将被转换为:

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

所以您需要将object结构更改为比当前更好。

为此您需要使用Array,这将帮助您在同一级别复制元素而不会出现任何问题。

我创建了一个简单的结构并为您的对象实现它,您可以使用另一个结构,但仍需要更改当前结构。

此代码===您当前的代码,但在其他结构中

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": []
            }
        ]
    }
  ]
}];

现在你的对象有一个很好的结构。您只需要创建一个为1个元素构建HTML字符串的函数。然后使用名为递归的技术(在内部调用函数)为所有嵌套元素调用它

这是您的案例,使用Object.keys()Array.prototype.reduce()

&#13;
&#13;
// 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;
&#13;
<div id="main"><div>
&#13;
&#13;
&#13;